被 foldLeft 错误所迷惑(在 Eclipse 和 REPL 中) [英] Confused by foldLeft error (both in Eclipse and REPL)

查看:35
本文介绍了被 foldLeft 错误所迷惑(在 Eclipse 和 REPL 中)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文非常简单.我的假设基于 Odersky 的书Programming in Scala, 2nd Edition",第 8.5 节描述了Placeholder Syntax".

The context for this is pretty simple. My assumptions are based on Odersky's book "Programming in Scala, 2nd Edition", section 8.5 describing "Placeholder Syntax".

我有一个 List[List[Boolean]](即矩形位图),我试图在其中计算值true"的总出现次数.这是定义执行良好的数据的 REPL 行:

I have a List[List[Boolean]] (i.e. a rectangular bit map) where I am attempting to count the total occurrences of the value "true". Here's the REPL line defining the data which executes fine:

val rowsByColumns =
    List(   List(false, true, false)
          , List(true, true, true)
          , List(false, true, false)
        )

接下来,我尝试使用以下行计算true"的出现次数.而不是执行,我收到一个错误:

Next, I attempted to count the occurrences of "true" with the following line. And instead of executing, I receive an error:

val marks = (for(row <- rowsByColumns)
    yield {row.foldLeft[Int](0)(_ + (if (_) 1 else 0))}).sum

<console>:8: error: wrong number of parameters; expected = 2
       val marks = (for(row <- rowsByColumns) yield {row.foldLeft[Int](0)(_ + (i
f (_) 1 else 0))}).sum
                                                                        ^

我不明白这个错误,因为我有两个下划线代表函数的参数.所以,我通过编写这个执行得很好的函数来使函数更加明确:

I did not understand the error as I have the two underscores representing the parameters to the function. So, I made the function more explicit by writing this which executes just fine:

val marks = (for(row <- rowsByColumns)
      yield {row.foldLeft[Int](0)((sum, marked) => sum + (if (marked) 1 else 0))}
    ).sum

我的问题是:为什么我在不太明确的情况下收到并出错,但是当我通过减少简化"来绘制函数时,它可以正确执行?

My question is this: Why did I receive and error for the less explicit case, but when I map out the function by reducing the "simplifications", it executes correctly?

感谢您对此的任何见解.

Thank you for any insight you can give me on this.

推荐答案

Scala 对匿名函数的占位符语法的局限性可能非常令人困惑(至少对我而言).一个经验法则是下划线绑定到最近的括号,但这是一个近似值 - 请参阅 Scala 规范了解详情:

The limitations of Scala's placeholder syntax for anonymous functions can be extremely confusing (to me, at least). One rule of thumb is that underscores get bound to their nearest enclosing parentheses, but this is an approximation—see section 6.23 of the Scala specification for the detail:

句法类别Expr 绑定一个下划线的表达式eu 部分,如果以下两个条件成立: (1) e 正确包含u,并且(2)没有其他的句法表达类别 Expr 正确包含在 e 中并且它本身正确包含u.

An expression e of syntactic category Expr binds an underscore section u, if the following two conditions hold: (1) e properly contains u, and (2) there is no other expression of syntactic category Expr which is properly contained in e and which itself properly contains u.

在这种情况下,编译器不会将第二个下划线视为第二个参数.这可能看起来很奇怪,因为 _ + _ 被正确地视为具有两个参数,并且 if (_) x else y 等价于 z =>if (z) x else y(其中 z 是一个新的标识符),但是嵌套两者不起作用.

In this case the compiler doesn't see the second underscore as a second parameter. This might seem odd, since _ + _ is properly seen as having two parameters, and if (_) x else y is equivalent to z => if (z) x else y (where z is a fresh identifier), but nesting the two doesn't work.

确实,编译器可以在理论上确定这两个下划线应该是foldLeft中同一个匿名函数的参数,但不是,例如,在以下,其中第二个下划线确实需要单独绑定:

It's true that the compiler could in theory figure out that the two underscores should be parameters for the same anonymous function in your foldLeft, but not, for example, in the following, where the second underscore really does need to be bound separately:

rowsByColumns.map(_.map(!_))

不过,这需要编译器方面非常聪明,而且 Scala 语言设计者认为这不值得——只需要为一些没有嵌套表达式的相当简单的情况提供占位符语法.

This would require a lot of extra cleverness on the part of the compiler, though, and the Scala language designers have decided that it's not worth it—that placeholder syntax only needs to be provided for some fairly simple cases without nested expressions.

幸运的是,在这种情况下,您可以改为编写 rowsByColumns.flatten.count(identity).flatten 此处连接子列表以提供单个 List[Boolean].然后我们想知道该列表中有多少值是 true.count 接受一个谓词并告诉您集合中有多少值满足该谓词.例如,这里有一种计算 1 到 10(含)之间偶数的方法:

Luckily in this case you can just write rowsByColumns.flatten.count(identity) instead. flatten here concatenates the sublists to give a single List[Boolean]. We then want to know how many of the values in that list are true. count takes a predicate and tells you how many values in a collection satisfy that predicate. For example, here's one way to count the even numbers between 1 and 10 (inclusive):

val isEven: Int => Boolean = _ % 2 == 0    
(1 to 10) count isEven

不过,在你的情况下,我们已经有了布尔值,所以谓词不需要做任何工作——它可以只是恒等函数 x =>;x.正如 dhg 在评论中指出的那样,Scala 的 Predef 对象 将此作为名为 identity 的方法提供,我在这里使用它.不过,如果您觉得更清楚,您也可以轻松地编写 rowsByColumns.flatten.count(x => x).

In your case, though, we already have boolean values, so the predicate doesn't need to do any work—it can just be the identity function x => x. As dhg notes in a comment, Scala's Predef object provides this as a method named identity, which I'm using here. You could just as easily write rowsByColumns.flatten.count(x => x), though, if you find that clearer.

这篇关于被 foldLeft 错误所迷惑(在 Eclipse 和 REPL 中)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆