【发布时间】:2014-07-03 06:42:50
【问题描述】:
麦耶罗!因此,我在 F# 中寻找一种简洁、高效的惯用方式来解析文件或字符串。我强烈倾向于将输入视为 char 序列(char seq)。这个想法是每个函数都负责解析输入的一部分,返回转换后的文本与未使用的输入元组,并由更高级别的函数调用,该函数将未使用的输入链接到以下函数并使用结果构建复合类型。因此,每个解析函数都应该有一个类似于这个的签名: char seq -> char seq * 'a 。例如,如果函数的职责只是提取第一个单词,那么一种方法如下:
let parseFirstWord (text: char seq) =
let rec forTailRecursion t acc =
let c = Seq.head t
if c = '\n' then
(t, acc)
else
forTailRecursion (Seq.skip 1 t) (c::acc)
let rest, reversedWord = forTailRecursion text []
(rest, List.reverse reversedWord)
当然,这种方法的主要问题是它以相反的顺序提取单词,因此您必须将其反转。然而,它的主要优点是使用严格的功能特性和适当的尾递归。可以避免提取值的反转,同时失去尾递归:
let rec parseFirstWord (text: char seq) =
let c = Seq.head t
if c = '\n' then
(t, [])
else
let rest, tail = parseFirstWord (Seq.skip 1 t)
(rest, (c::tail))
或者在下面使用一个快速可变的数据结构,而不是使用纯粹的函数特性,例如:
let parseFirstWord (text: char seq) =
let rec forTailRecursion t queue =
let c = Seq.head t
if c = '\n' then
(t, queue)
else
forTailRecursion (Seq.skip 1 t) (queue.Enqueu(c))
forTailRecursion text (new Queue<char>())
我不知道如何在 F# 中使用 OO 概念,因此欢迎对上述代码进行更正。
作为这种语言的新手,我希望在 F# 开发人员通常做出的妥协方面得到指导。在建议的方法和您自己的方法中,我应该认为哪个更惯用,为什么?此外,在这种特殊情况下,您将如何封装返回值:char seq * char seq、char seq * char list 或 evenchar seq * Queue<char>?或者你甚至会考虑 char seq * String 在适当的转换之后?
【问题讨论】:
-
对于更复杂的任务,这个解析器生成器工具可以帮助你:fsharppowerpack.codeplex.com/…
标签: f# tail-recursion