匿名函数参数在 SML 的 foldl 中的位置

问题描述

我需要编写一个函数,它接受一个字符串列表并找到列表中最大的字符串。在平局的情况下,它应该返回最接近列表末尾的字符串。问题是它需要使用 List.foldl 遍历列表,并且除了 List,foldl 的库函数中的那些之外,不能使用递归调用。 以下代码工作正常。

fun longest_string2 str_list =
     List.foldl(fn (x,acc) => if String.size x >= String.size acc then x else acc) "” str_list

如果我在 REPL 中运行 longest_string2 ["Apple","ball","Bpple”];,我得到 val it = "Bpple" : string 但是,如果我如下反转匿名函数的参数,我会得到 val it = "Apple" : string。 由于匿名函数是按名称而不是位置访问元素,为什么会有所不同?

解决方法

List.foldl 的定义是

fun foldl (f: 'a*'b->'b) (acc: 'b) (l: 'a list): 'b =
  case l of
    [] => acc
  | x::xs => foldl f (f(x,acc)) xs

如果您反转匿名函数的参数,您的函数将变为:(如果我误解了您的问题,请纠正我)

fun longest_string2 str_list =
     List.foldl(fn (acc,x) => if String.size x >= String.size acc then x else acc) "” str_list

如果您现在将 ["Apple","ball","Bpple”] 作为参数传递给 longest_string2foldl 函数会将您的列表与 x::xs 进行模式匹配,其中 x 是“Apple”并且xs 是 ["ball","Bpple"]。当您使用 f(x,acc) 计算更新的累加器时,xacc 被交换。换句话说,在您的匿名函数(带有反向参数),您会假设第一个参数是 ””,第二个参数是 Apple 但 List.foldl 的实现将通过 f(“Apple”,“”)。在这个在这种情况下,您的匿名函数会将 “Apple” 标记为 “acc”,将 “” 标记为 “x”

,

@3123 对问题的回答最多,但没有直接解决问题中的这一陈述。

由于匿名函数是按名称访问元素而不是 位置,为什么这会有所不同?

foldl 接受一个函数,该函数将一个 tuple 作为它的参数,这是位置。

如果我们真的想实现这一点,我们可以定义一个以记录作为参数的折叠函数:

fun namedFold _ acc [] = acc
  | namedFold f acc (x::xs) =
      namedFold f (f {acc=acc,x=x}) xs;

然后将其称为:

namedFold (fn { acc,x } => acc + x) 0 [1,2,3,4]

或作为:

namedFold (fn { x,acc } => acc + x) 0 [1,4]

得到同样的结果。

但是 namedFold 的类型是 fn :({acc: 'a,x: 'b} -> 'a) -> 'a -> 'b list -> 'a 并且基本上不可能轻松地将现有函数传递给它。使用 foldl 的定义方式,我们可以轻松地将之前对 namedFold 的调用重写为:

foldl op+ 0 [1,4]

因为 op+foldl 的第一个参数具有相同的签名。