Functional programming F将作用于不同源流的解析器绑定或组合在一起
如何组合解析器函数,使它们在不同的源流上执行,而后面的函数则取决于前面的结果? 说下面两个:Functional programming F将作用于不同源流的解析器绑定或组合在一起,functional-programming,f#,fparsec,Functional Programming,F#,Fparsec,如何组合解析器函数,使它们在不同的源流上执行,而后面的函数则取决于前面的结果? 说下面两个: let outerP = many (pchar 'a') |>> (fun l -> l.Length) let innerP x = pstring "something" |>> (fun str -> (str,x)) 对于单个源,绑定工作得很好: let combinedP = outerP >>= innerP run combinedP "
let outerP = many (pchar 'a') |>> (fun l -> l.Length)
let innerP x = pstring "something" |>> (fun str -> (str,x))
对于单个源,绑定工作得很好:
let combinedP = outerP >>= innerP
run combinedP "aasomething"
但是作为一个更复杂项目的一部分,我需要一起解析几个单独的文件,后面的解析器使用前面的输出。
我有
let outerSource = "aaaaa"
let innerSource = "something"
显而易见的解决方案是将文件连接在一起,但扩展性不强,特别是因为实际上有一个内部源文件列表,等等
背景:我是函数式编程新手,不确定这是否会让函数组合走得太远,但似乎这应该是一个很好的解决方案,只是在这种情况下我无法理解。
下面是一个工作但非功能性的解决方案,它将在实际项目中产生一个多层嵌套代码
如何处理单独的源文件:
let combinedScript =
let outerR = run outerP outerSource
match outerR with
| Success (outerParam,_,_) ->
let innerR = run (innerP outerParam) innerSource
innerR
在真正的代码中,这是一个4级的末日金字塔,看看它,它基本上就是bind所做的,只是做了一个额外的更改不同的源代码你的最后一句话包含了一个很好的功能方法的线索:。。。从这个角度看,它基本上就是bind所做的,只是对不同的源代码做了额外的更改 让我们通过实现我们自己的bind-like函数,将您的四级末日金字塔变成一个好看的表达式。我将使用您的组合脚本表达式,并将outerP和outerSource以及innerP和innerSource转换为函数参数,希望您对结果感到满意
let combinedScript (outerP, outerSource) (innerP, innerSource) =
let outerR = run outerP outerSource
match outerR with
| Success (outerParam,_,_) ->
let innerR = run (innerP outerParam) innerSource
innerR
| Failure (msg, err, state) ->
Failure (msg, err, state)
// And we'll define an operator for it
let (>==>) (outerP, outerSource) (innerP, innerSource) =
combinedScript (outerP, outerSource) (innerP, innerSource)
// Now you can parse your four files like this
let parseResult =
(parserA, fileA)
>==> (parserB, fileB)
>==> (parserC, fileC)
>==> (parserD, fileD)
函数式编程真正伟大的地方在于,我写了上面的内容,甚至不必去想它,因为将厄运金字塔转化为平面列表是一个众所周知的诀窍。正如你所说,bind基本上就是这么做的。我在上面所做的就是遵循编写绑定函数的标准方法。如果您还不知道绑定函数的标准配方,这是我找到的最好的解释。如果你和我一样,你必须读四五遍,然后你的脑子里才会有东西咔嗒咔嗒地响,但一旦你有了它,啊哈!现在,您将对函数式编程的威力有更深入的了解,以及它如何让您简单地完成真正高级的事情
另外,如果该系列文章对于您目前对FP的理解来说过于超前,那么请尝试。其中一个可能是更好地介绍这些概念,这取决于你已经理解了多少。你的最后一句话包含了一个很好的功能方法的线索:。。。从这个角度看,它基本上就是bind所做的,只是对不同的源代码做了额外的更改 让我们通过实现我们自己的bind-like函数,将您的四级末日金字塔变成一个好看的表达式。我将使用您的组合脚本表达式,并将outerP和outerSource以及innerP和innerSource转换为函数参数,希望您对结果感到满意
let combinedScript (outerP, outerSource) (innerP, innerSource) =
let outerR = run outerP outerSource
match outerR with
| Success (outerParam,_,_) ->
let innerR = run (innerP outerParam) innerSource
innerR
| Failure (msg, err, state) ->
Failure (msg, err, state)
// And we'll define an operator for it
let (>==>) (outerP, outerSource) (innerP, innerSource) =
combinedScript (outerP, outerSource) (innerP, innerSource)
// Now you can parse your four files like this
let parseResult =
(parserA, fileA)
>==> (parserB, fileB)
>==> (parserC, fileC)
>==> (parserD, fileD)
函数式编程真正伟大的地方在于,我写了上面的内容,甚至不必去想它,因为将厄运金字塔转化为平面列表是一个众所周知的诀窍。正如你所说,bind基本上就是这么做的。我在上面所做的就是遵循编写绑定函数的标准方法。如果您还不知道绑定函数的标准配方,这是我找到的最好的解释。如果你和我一样,你必须读四五遍,然后你的脑子里才会有东西咔嗒咔嗒地响,但一旦你有了它,啊哈!现在,您将对函数式编程的威力有更深入的了解,以及它如何让您简单地完成真正高级的事情
另外,如果该系列文章对于您目前对FP的理解来说过于超前,那么请尝试。其中之一可能是更好地介绍这些概念,具体取决于您已经了解的程度。首先,为什么您认为您的解决方案不起作用?实用并不意味着美丽或优雅。功能代码可能和面向对象的代码一样丑陋和复杂。仅仅是因为它是一个末日金字塔的事实并没有使它失去功能 其次,这几乎不是bind在做什么,而是bind在做什么。事实上,你有额外的价值,在那里没有改变这一点。事实上,如果绑定函数只能使用它们的直接输入,那么绑定的有用性将相当有限 为了避免厄运金字塔,您可以很好地利用F语法。例如,这项工作:
let x =
20 |> fun a ->
a + 1 |> fun b ->
b * 2
// x == 42
本例使用两个嵌套函数,将前一个函数的结果传递给下一个函数。它可以重写为:
let x = (fun a -> (fun b -> b * 2) (a + 1)) 20
但我利用操作符|>和F越位规则使它看起来像是一步一步的计算
如果您定义了一个类似的合成操作符,那么您也可以做类似的事情
解析器结果:
注意,我的操作符|>>=的实现丢弃了“UserState”并定位成功的最后两个参数。如果你不在乎这些,这个解决方案就足够了。否则,您需要弄清楚如何将res中的内容与fx返回的内容组合起来。首先,为什么您认为您的解决方案不起作用?实用并不意味着美丽或优雅。功能代码可能和面向对象的代码一样丑陋和复杂。仅仅是因为它是一个末日金字塔的事实并没有使它失去功能 其次,这几乎不是bind在做什么,而是bind在做什么。事实上,你有额外的价值,在那里没有改变这一点。事实上,如果绑定函数只能使用它们的直接输入,那么绑定的有用性将相当有限 为了避免厄运金字塔,您可以很好地利用F语法。例如,这项工作:
let x =
20 |> fun a ->
a + 1 |> fun b ->
b * 2
// x == 42
本例使用两个嵌套函数,将前一个函数的结果传递给下一个函数。它可以重写为:
let x = (fun a -> (fun b -> b * 2) (a + 1)) 20
但我利用操作符|>和F越位规则使它看起来像是一步一步的计算
如果为ParseResult定义类似的合成运算符,则可以执行类似的操作:
注意,我的操作符|>>=的实现丢弃了“UserState”并定位成功的最后两个参数。如果你不在乎这些,这个解决方案就足够了。否则,您需要找出如何将res中的内容与f x返回的内容相结合。标记这一点是为了好的学习材料,但这两个答案都有帮助,肯定是在我必须重读它们的阶段。标记这一点是为了好的学习材料,但这两个答案都有帮助,肯定是在我必须重读的阶段。