@@ -1045,6 +1045,81 @@ fn parent_element<'a>() -> impl Parser<'a, Element> {
10451045}
10461046```
10471047
1048+ Oops。现在我们要怎么把参数传给` close_element ` ?我想我们还缺少最后一个组合子。
1049+
1050+ 我们已经很接近了。一旦我们解决了最后这个问题以让` parent_element ` 工作,我们就能把` element ` 解析器的` open_element ` 替换成新的` parent_element ` ,这就意味着,我们就有一个完整可用的XML解析器。
1051+
1052+ 还记得我前面说过我们后面会回到` and_then ` 吗?好的,后面就是这里了。我们所需要的组合子,实际上,就是` and_then ` :我们需要一个东西能接收一个解析子,还要一个函数,这个函数能接收解析子的返回结果并返回一个* 新* 解析子,这个解析子我们后面再用。这有点像` pair ` ,但不是仅仅收集tuple里的两个结果,我们把结果传入一个函数。这也是` and_then ` 怎么作用在` Result ` 和` Options ` 的方式,除了这个更好理解,因为` Result ` 和` Options ` 基本上什么也不做,它们只是包含了一些数据而已(或者不包含,在某一些情况下)。
1053+
1054+ 所以让我们试着实现一下它。
1055+
1056+ ``` rust
1057+ fn and_then <'a , P , F , A , B , NextP >(parser : P , f : F ) -> impl Parser <'a , B >
1058+ where
1059+ P : Parser <'a , A >,
1060+ NextP : Parser <'a , B >,
1061+ F : Fn (A ) -> NextP ,
1062+ {
1063+ move | input | match parser . parse (input ) {
1064+ Ok ((next_input , result )) => f (result ). parse (next_input ),
1065+ Err (err ) => Err (err ),
1066+ }
1067+ }
1068+ ```
1069+
1070+ 看看这些类型,这里有很多类型变量,但是我们认识` P ` ,我们的输入解析器,` P ` 有一个结果类型` A ` 。我们的函数` F ` ,本来是一个从` A ` 到` B ` 的` map ` ,然而,关键的不同在于` and_then ` 接收一个函数,这个函数把` A ` 转化为一个新的解析器` NextP ` ,` NextP ` 有一个返回类型` B ` 。最终的结果类型是` B ` ,因此我们可以假设不管从` NextP ` 返回的是什么,将是最后的结果。
1071+
1072+ 这段代码没类型那么复杂:我们一开始运行输入解析器,如果失败,这段code也失败,但是如果输入解析器成功,接下来我们调用函数` f ` 作用于result(为类型` A ` ),从` f(result) ` 返回的是个新的解析器, 类型是` B ` 。我们把返回的解析器作用在下一段输入,然后直接返回结果。如果失败,就挂在这里,如果成功,我们就得到类型为` B ` 的值。
1073+
1074+ 再来一次:首先我们运行类型为` P ` 的解析器,如果成功,我们调用函数` F ` 作用在解析器` P ` 的结果上,得到下一个类型为` NextP ` 的解析器,然后我们运行,得到最终的结果。
1075+
1076+ 让我们也把它直接加到` Parser ` 特质上,因为这个就像` map ` ,肯定这样读起来更好。
1077+
1078+ ``` rust
1079+ fn and_then <F , NextParser , NewOutput >(self , f : F ) -> BoxedParser <'a , NewOutput >
1080+ where
1081+ Self : Sized + 'a ,
1082+ Output : 'a ,
1083+ NewOutput : 'a ,
1084+ NextParser : Parser <'a , NewOutput > + 'a ,
1085+ F : Fn (Output ) -> NextParser + 'a ,
1086+ {
1087+ BoxedParser :: new (and_then (self , f ))
1088+ }
1089+
1090+ ```
1091+
1092+ OK,现在,这样做有什么好处?
1093+
1094+ 首先,我们几乎可以用这个实现` pair ` :
1095+
1096+ ``` rust
1097+ fn pair <'a , P1 , P2 , R1 , R2 >(parser1 : P1 , parser2 : P2 ) -> impl Parser <'a , (R1 , R2 )>
1098+ where
1099+ P1 : Parser <'a , R1 > + 'a ,
1100+ P2 : Parser <'a , R2 > + 'a ,
1101+ R1 : 'a + Clone ,
1102+ R2 : 'a ,
1103+ {
1104+ parser1 . and_then (move | result1 | parser2 . map (move | result2 | (result1 . clone (), result2 )))
1105+ }
1106+ ```
1107+
1108+ 看上去很整洁,但是这里有个问题:` parser2.map() ` 消耗了` parser2 ` 来创建包装解析器,而且函数是` Fn ` ,不是` FnOnce ` ,因此不允许消费` parser2 ` ,只能取它的引用。Rust的问题,换句话说。在一个这些不是问题的更高级的语言中,这可以是一个很整洁的定义` pair ` 的方法。
1109+
1110+ 尽管如此,我们还是能在Rust用这个函数懒生成` close_element ` 解析器的正确版本,或者,换句话,我们可以把参数传入它。
1111+
1112+ 回想一下我们失败的尝试:
1113+
1114+ ``` rust
1115+ fn parent_element <'a >() -> impl Parser <'a , Element > {
1116+ pair (
1117+ open_element (),
1118+ left (zero_or_more (element ()), close_element (…oops )),
1119+ )
1120+ }
1121+ ```
1122+
10481123通过使用 ` and_then ` ,我们现在可以通过使用该函数构建正确版本的 ` close_element ` 来获得正确的结果。
10491124
10501125``` rust
0 commit comments