F# 我不理解这个代码

F# 我不理解这个代码,f#,recursion,pattern-matching,tail-recursion,F#,Recursion,Pattern Matching,Tail Recursion,我是F#的新手,找到了一些我想使用的代码。这段代码获取一个列表并返回该列表的后半部分。我希望有人能逐行检查一下它的功能。我想更改它,使其返回列表的前半部分。这是我的问题之后的代码 let cut l = let rec cut = function | xs, ([] | [_]) -> xs | [], _ -> [] | x::xs, y::y'::ys -> cut (xs, ys) cut (l, l) =函

我是F#的新手,找到了一些我想使用的代码。这段代码获取一个列表并返回该列表的后半部分。我希望有人能逐行检查一下它的功能。我想更改它,使其返回列表的前半部分。这是我的问题之后的代码

let cut l = 
   let rec cut = function
       | xs, ([] | [_]) -> xs  
       | [], _ -> []
       | x::xs, y::y'::ys -> cut (xs, ys)
   cut (l, l)
=函数的作用是什么

我很确定如果有
xs
将它添加到列表中,那么
|xs,([]|[|]]->xs
就是

我不明白这是怎么回事


| x::xs,y::y'::ys->cut(xs,ys)
:我理解前半部分,它创建了两个子列表,让我困惑的是为什么cut发送尾部
xs
,以及
ys
。剪切是否只获取一个参数?

该函数返回给定列表的后半部分

代码中有趣的部分只是嵌套(递归)函数,因为外部函数的唯一目的是调用嵌套函数并将指定列表传递给它两次。嵌套的
cut
函数有两个参数(作为元组),因此其类型为:

cut : 'a list * 'a list -> 'a list
当递归调用自身时,这就是被调用的函数(这解释了为什么用两个参数调用它)。以下是注释代码:

// The 'function' syntax means that the arguments of the function are matched against 
// several clauses. When the arguments (lists) match the clause, the clause is selected
// and its body will be executed. 
let rec cut = function
  // When the second list is empty or contains a single element,
  // the function return all elements of the first list
  | xs, ([] | [_]) -> xs  
  // When the first list is empty, return empty list
  | [], _ -> []
  // When first list is non-empty and second contains at least two elements,
  // the function takes one element from the first list and two elements from 
  // the second list (x, y, y'), ignores them and calls itself with the 
  // remaining lists as arguments.
  | x::xs, y::y'::ys -> cut (xs, ys)

cut ([ 1 .. 10 ], [ 1 .. 10 ])
该函数的思想是迭代同一列表的两个副本。在每个递归步骤中,它从第二个步骤跳过两个元素,从第一个步骤跳过一个元素。当它到达第二个列表的末尾时,第一个列表包含了列表的后半部分(因为函数跳过其元素的速度慢了2倍)

要获取列表的前半部分需要向内部递归
cut
函数添加额外的参数。您可以使用它来累积第一个列表中的元素。同样,您需要以两倍的速度跳过其中一个列表的元素。您不需要跳过其他列表的第一个元素,而是需要获取它们并将它们添加到累加器中

我不会给你一个完整的解决方案,但为了给你一些想法,这里有一些伪代码:

  | x::xs, _::_::ys -> 
      // Call 'cut' recursively to process 'xs' and 'ys'
      // and add the element 'x' to the accumulator.
编写函数的另一种方法是使用
match
而不是
function
,并将这两个参数作为标准多参数编写(而不是使用元组)。在忽略最后一个子句中的元素时,也可以使用
\uuu
,因为不需要它们的名称:

let rec cut l1 l2 = 
  match l1, l2 with
  | xs, ([] | [_]) -> xs  
  | [], _ -> []
  | _::xs, _::_::ys -> cut xs ys

cut [ 1 .. 10 ] [ 1 .. 10 ]

更改它以返回列表的前半部分的最简单方法是:将反转的列表传递到内部函数并反转结果

let cut l = 
  let rec cut = function
    | xs, ([] | [_]) -> xs  
    | [], _ -> []
    | x::xs, y::y'::ys -> cut (xs, ys)
  let k = List.rev l
  cut (k, k) |> List.rev
无列表。修订版
查看cut函数正在做什么的最简单的方法是认识到下面的行

| x::xs, y::y'::ys -> cut (xs, ys)

清空第二个列表的速度是第一个列表的两倍。这是因为它正在从
ys
列表的头上取下2个元素,从
xs
列表的头上取下一个元素并将它们扔掉。如果它继续这样做,那么
ys
将首先终止,当它终止时
xs
将包含原始列表的下半部分。

如果不使用列表,您将如何处理。rev?@Aaron:请阅读您原始帖子中的评论。我有点认为这是家庭作业,但嘿,当你解释这个解决方案时,你已经得到了。这会让你朝着一个新的方向思考。只是想澄清一下——这是一个家庭作业的问题吗?如果是的话,我们仍然很乐意为您提供帮助&给您一些建议,但不是一个完整的解决方案(您可以开始销售:-)。这是作业的一部分,正如我所提到的,我正在试图弄清楚它是如何工作的,以便我可以操作它。由于某种原因,当我使用调试器时,它不会进入函数。因此我对此感到困惑如果您使用
match
语法(您也可以更轻松地添加
printf
以打印一些调试信息),调试器可能会工作得更好。@Tomas我将其更改为match,并且由于某种原因调试器无法处理此函数,它在othersTook上运行得很好,但我终于明白了为什么你会得到列表的后半部分
| x::xs, y::y'::ys -> cut (xs, ys)