【问题标题】:Ocaml take functionOcaml 取函数
【发布时间】:2017-03-04 19:14:18
【问题描述】:

我正在尝试创建一个返回列表中前 k 个元素的值的函数。这是行不通的。我不知道为什么。

有人可以帮忙吗?

这是 tl 函数 -

let tl j = match j with
  | [] -> []
  | x :: xs -> xs;;

这是真正的拍摄功能-

let rec take k xs = function
  | [] -> failwith "take"
  | x :: xs -> if k = List.length ([x] @ xs) then [x] @ xs
               else [x] @ take List.length xs (tl(List.rev.xs))

【问题讨论】:

  • (如果您输入代码而不是张贴图片,测试代码会容易得多。)

标签: function ocaml take


【解决方案1】:

您的函数接受一个整数 n,并从列表 list 中获取 n 个元素,该列表包含 list 的 n 个第一个元素。 所以你的签名很可能是:

int -> 'a list -> 'a list = <fun>

您可能已经猜到该函数将被递归定义,因此您已经可以像这样编写函数的定义:

let rec take n list = ...

您必须建立一个列表,但让我们首先考虑 n 的三大类值:

  • n = 0 : 只返回一个空列表
  • n > 0 : 取一个元素并用 n - 1 递归
  • n : 否定的情况很容易漏掉,但是如果你不小心的话,很容易在运行时输入无效输入的无限递归。幸运的是,在这里,我们将使用异常并使事情沿递归链终止(例如,moldbino's answer 中发生的情况),但其他函数可能表现不佳。 take 的一些实现,当给定一个负数时,尝试有用并从列表末尾获取 n 个元素。 在这里,我们将简单地将任何不是严格正数的 n 视为 null。

空或负N

当我们从列表中取出零个元素时,这意味着我们返回空列表:

let rec take n list =
  if n > 0
  then ...
  else [];;

正面N

现在,我们考虑从列表中获取 n > 0 个元素的情况。 根据列表的递归定义,我们必须考虑每种可能的列表,即空列表非空列表

let rec take n list =
  if n > 0
  then
    match list with
    | [] -> ...
    | x :: xs -> ...
  else [];;

当我们想从一个空列表中取出 n > 0 个元素时会发生什么? 我们失败了。

let rec take n list =
  if n > 0 then
    match list with
    | [] -> failwith "Not enough elements in list"
    | x :: xs -> ...
  else [];;

现在,递归的一般情况。我们有一个非空列表,并且 n > 0,所以我们知道我们可以从列表中获取至少一个元素。 这个值是x,它构成了我们要返回的列表的头部。列表的尾部是由 xsn-1 个元素组成的,take 本身很容易计算出来:

let rec take n list =
  if n > 0 then
    match list with
    | [] -> failwith "Not enough elements in list"
    | x :: xs -> x :: (take (n - 1) xs)
  else [];;

【讨论】:

    【解决方案2】:

    该消息的原因是您遗漏了一对括号,因此您尝试将List.length 作为第一个参数传递给take

    另一个问题是你定义了一个带有三个参数的函数——你的定义说take k xs返回一个带有一个列表参数(未命名)的函数。

    第三个问题是take (List.length xs) (tl(List.rev xs))
    这试图比xs 的尾部多一个元素(并且由于某种原因,相反)。

    所以我要完全重写它。

    首先,您不应该为此使用List.length
    List.length 几乎从来都不是一个好的解决方案。)
    你只需要关心列表是否为空,不管它有多长。

    现在,一个更正式的定义:

    take k xs

    • 如果k为0,则为空列表
    • 否则,如果xs为空列表,则报错
    • 否则,它的第一个元素是xs 的头部,其尾部是从xs 的尾部取k - 1 元素的结果。

    也就是说,

    let rec take k xs = match k with
        | 0 -> []
        | k -> match xs with
               | [] -> failwith "take"
               | y::ys -> y :: (take (k - 1) ys)
    

    【讨论】:

      【解决方案3】:

      您的直接问题是您需要在(List.length xs) 周围加上括号。该代码还在所有情况下都将空输入列表视为错误,这是不正确的。如果所需长度 k 为 0,则空列表是合法输入。

      更新

      您的定义中还有一个额外的参数。如果你这样说:

      let myfun k xs = function ...
      

      函数myfun 有3 个参数。前两个被命名为kxs,第三个是function 表达式的隐含部分。

      快速解决方法就是删除xs

      我认为您可能在对take 的递归调用中要求的元素数量错误。无论如何都要看看。

      【讨论】:

      • let rec take k xs = function |[] -> failwith "take" |x :: xs -> if k = List.length ([x] @ xs) then [x] @ xs else [x] @take (List.length xs) (tl(List.rev xs))
      • 出现此错误 -------- 错误:此表达式的类型为 'a list -> 'a list 但表达式应为 'a list 类型
      • 你的函数在它的声明中有一个额外的参数。查看更新的答案。
      猜你喜欢
      • 2012-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多