【问题标题】:How to use List.filter?如何使用 List.filter?
【发布时间】:2023-10-25 09:30:01
【问题描述】:

我有这个代码来过滤第一个字母是大写的字符串列表:

fun f s = Char.isUpper(String.sub(s,0));
fun only_capitals (xs : string list) =  List.filter(f , xs);

但是编译的时候总是报错:

operator domain: 'Z -> bool
operand:         (string -> bool) * string list
  in expression:
    List.filter (f,xs)

这个错误是什么意思?如何解决?

【问题讨论】:

  • 这似乎是 Dan Grossman (Coursera/U Washington) 的编程语言作业 hw #3,问题 #1。 “编写一个函数only_capitals,它接受一个字符串列表并返回一个字符串列表,该列表仅包含参数中以大写字母开头的字符串。假设所有字符串至少有1个字符。使用List.filterChar.isUpper,和String.sub 制作 1-2 行解决方案。”
  • 这可能值得参考 Grossman 的第 13 页导致这项作业的讲义。 “例如, List.foldl((fn (x,y) => x+y),0,[3,4,5]) 不会进行类型检查,因为 List.foldl 需要 'a * 'b -> 'b 函数,不是三元组。正确的调用是 List.foldl (fn (x,y) = x+y) 0 [3,4,5]..." 它使用柯里化,是推力。这也让我绊倒了好一阵子。
  • 强大的一行:List.filter(fn x => Char.isUpper(String.sub(x, 0))) xs

标签: list filter sml


【解决方案1】:

List.filter 的类型签名是

val filter : ('a -> bool) -> 'a list -> 'a list

所以你需要给List.filter 两个不同的参数,不是一个恰好是元组的参数

【讨论】:

  • 就像你在 ML 中遇到的功能一样 ;-)
  • @pad 如果我错了,请纠正我,这不是 两个 参数,只是用第一个参数调用 List.filter 来获取一个函数,然后调用返回的 带有第二个参数的函数,对吧?
【解决方案2】:

您需要将其更改为:

fun only_capitals (xs : string list) =  List.filter f xs

filter 有 2 个参数,一个函数 f ('a -> bool) 和一个列表。

在 ML 中传递元组的语法很容易与其他语言中的函数式应用程序的语法混淆。

你也可以这样定义:

val only_capitals = List.filter f

【讨论】:

  • 我相信更合适的措辞是 filter 接受 1 个参数(一个函数)并返回一个接受列表的函数。过滤器不接受 2 个参数。来自文档: val filter : ('a -> bool) -> 'a list -> 'a list
【解决方案3】:

ML 中的函数只能接受 一个 参数。来自here 的描述(另请参见此处的注释和视频)。

List.filter 是所谓的柯里化函数,所以List.filter f xs 实际上是(List.filter f) xs 其中List.filter f 是一个函数。我们必须提供f (fn: a -> bool) 作为List.filter 的参数,而不是元组(f, xs)

这是一个简单的例子。当我们调用is_sorted 1 时,我们会在其环境中得到一个带有x 的闭包。当我们用 2 调用这个闭包时,我们得到true,因为1 <= 2

val is_sorted = fn x => (fn y => x <= y)
val test0 = (is_sorted 1) 2

val is_sorted = fn : int -> int -> bool
val test0 = true : bool

【讨论】:

    【解决方案4】:

    在 SML 文档中,它指出:

    过滤器 从左到右将 f 应用于 l 的每个元素 x,并返回 f x 评估为真的 x 的列表,其顺序与它们在参数列表中出现的顺序相同。

    所以它是一个柯里化函数。

    【讨论】:

      【解决方案5】:

      在SML文档中,List结构中的过滤函数列为

      filter f l
      

      在哪里需要柯里化参数 fl

      您必须提供一个函数和由空格分隔的列表,而不是在元组中传递参数。答案会是这样的

      fun only_capitals (xs: string list) = 
          List.filter (fn s => Char.isUpper(String.sub(s,0))) xs
      

      【讨论】: