我正在尝试在F#中实现一个用于将列表拆分为两个等长的一半(该问题假定列表长度均匀)的函数。使用搜索功能产生了一个线程,处理我正在尝试解决的完全相同的问题:
Split list into two equal lists in F#
我正在尝试实施用户Juliet提供的解决方案,他提供了部分答案:
let cut l =
let rec cut = function
| xs, ([] | [_]) -> xs
| [], _ -> []
| x::xs, y::y'::ys -> cut (xs, ys)
cut (l, l)
返回列表的后半部分。我正试图找到一种方法来返回上半场,所以我做了一些修改:
let rec cut(xs, ys) =
let zs = []
match xs, ys with
| xs, ([] | [_]) -> (xs, zs)
| [], _ -> ([], [])
| x::xs, y1::y2::ys ->
x::zs
cut (xs, ys)
你可能会说F#是我的第一个函数式编程语言,而且我对它的工作原理感到困惑。模式匹配对我来说是一件新事物,我从来都不擅长递归。
根据我的理解,模式匹配的工作方式是它基本上是if-then-else语句的更通用版本,其中 - >左边的代码。是条件(“if”位),右边的所有内容都是在条件检查通过时执行的代码块(“then”位)。
我不能完全确定你是否被允许做这样的事情:
| x::xs, y1::y2::ys ->
x::zs
cut (xs, ys)
如果模式匹配确实像if语句一样工作,那么它应该是,因为在if语句中它看起来像
if (x::xs && y1::y2::ys)
{
x::zs
cut (xs, ys)
}
无论如何,它似乎不允许声明x :: zs,因为Visual Studio给了我一个警告:
隐式忽略此表达式的结果。考虑使用“忽略”来明确地丢弃此值,例如'expr |>忽略',或'让'将结果绑定到名称,例如让result = expr'。
我不确定最后一部分意味着什么。我以为我已经将列表声明为行
中函数的局部变量let zs = []
我想要做的就是在cut函数的每次递归迭代中取出列表xs的头部并将其添加到另一个列表zs,当达到基本情况时,它将包含传递的每个元素x(换句话说,列表的前半部分,然后返回两个xs(包含列表的后半部分)和包含前半部分的列表,但它似乎不允许这样做?
答案 0 :(得分:2)
无论如何,它似乎不允许声明x :: zs,因为Visual Studio会给我一个警告。
允许表达式 x::zs
,但Visual Studio试图告诉您它没有效果。 x::zs
会创建一个新列表,但会立即丢弃它(不会改变现有值!)。
根据提供的其余代码,zs
应该包含列表的前半部分,但zs
只会包含空列表[]
,因为这样做了它所分配的唯一值。在像C#这样的语言中,您只需通过附加变更列表,但在F#中您不应该这样做。由于您已经在使用递归,因此zs
应该是递归函数的参数,以便您以后可以使用新值。 (我发现有些人如果把它想象成回调就会更好地理解递归。当你递归时,它就像为回调函数提供参数一样。在递归过程中你想要积累的任何值都需要作为函数的参数提供。)
把这一切放在一起,我想你想要的是这样的:
let rec cut(xs, ys, zs) =
match xs, ys with
| xs, ([] | [_]) -> (xs, zs)
| [], _ -> ([], [])
| x::xs, y1::y2::ys ->
cut (xs, ys, x::zs)
通常,您将此函数隐藏在非递归函数中,该函数可正确设置参数:
let cut(xs, ys) =
let rec impl(xs, ys, zs) =
match xs, ys with
| xs, ([] | [_]) -> (xs, zs)
| [], _ -> ([], [])
| x::xs, y1::y2::ys ->
impl (xs, ys, x::zs)
impl(xs, ys, [])
注意:没有必要使用,
来分隔F#中的参数。当你这样做时,你实际上声明一个函数有一个由元组组成的参数。这通常不是你想要的,它可以防止功能崩溃。你需要用来分隔参数的只是空格:
let cut xs ys =
let rec impl xs ys zs =
match xs, ys with
| xs, ([] | [_]) -> (xs, zs)
| [], _ -> ([], [])
| x::xs, y1::y2::ys ->
impl xs ys (x::zs)
impl xs ys []
答案 1 :(得分:1)
这是通过添加另一个累加器值来实现它的方法,该值将慢项构建到列表中:
let cut l =
let rec loop acc rem =
match rem with
| xs, ([] | [_]) -> List.rev acc, xs
| [], _ -> [], []
| x::xs, y::y'::ys -> loop (x::acc) (xs, ys)
loop [] (l, l)
> cut [1;2;3;4;5;6]
([1; 2; 3], [4; 5; 6])