8

このコンテキストは非常に単純です。私の仮定は、Oderskyの著書「Programmingin Scala、2nd Edition」、セクション8.5「プレースホルダー構文」に基づいています。

List [List [Boolean]](つまり、長方形のビットマップ)があり、値「true」の出現回数の合計をカウントしようとしています。正常に実行されるデータを定義するREPL行は次のとおりです。

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

次に、次の行で「true」の発生をカウントしようとしました。そして、実行する代わりに、エラーが発生します。

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
                                                                        ^

関数のパラメーターを表す2つのアンダースコアがあるため、エラーを理解できませんでした。それで、私はこれをうまく実行するように書くことによって関数をより明確にしました:

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

私の質問はこれです:なぜ私はあまり明確でないケースでエラーを受け取ったのですか?しかし、「単純化」を減らして関数をマップすると、正しく実行されますか?

あなたがこれについて私に与えることができるどんな洞察にも感謝します。

4

3 に答える 3

13

匿名関数に対するScalaのプレースホルダー構文の制限は、(少なくとも私にとっては)非常に混乱する可能性があります。経験則の1つは、アンダースコアは最も近い括弧にバインドされるということですが、これは概算です。詳細については、Scala仕様のセクション6.23を参照してください。

構文カテゴリの式eは、次の2つの条件が当てはまる場合、アンダースコアセクションuをExpr バインドします。(1) eにuが適切に含まれ、(2) eに適切に含まれ、それ自体に適切に含まれる構文カテゴリの他の式がないuExpr

この場合、コンパイラーは2番目のアンダースコアを2番目のパラメーターとして認識しません。これは奇妙に思えるかもしれません。これは、_ + _2つのパラメーターがあると適切に見なされ、 (は新しい識別子)if (_) x else yと同等ですが、2つをネストすることは機能しません。z => if (z) x else yz

理論的には、コンパイラ2つのアンダースコアが、の同じ無名関数のパラメータである必要があることを理解できますがfoldLeft、たとえば、2番目のアンダースコアを実際に個別にバインドする必要がある次の場合はそうではありません。

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

ただし、これにはコンパイラー側で多くの巧妙さが必要であり、Scala言語の設計者は、それだけの価値はないと判断しました。プレースホルダー構文は、ネストされた式のない非常に単純なケースにのみ提供する必要があります。


幸いなことに、この場合はrowsByColumns.flatten.count(identity)代わりに書くことができます。flattenここでは、サブリストを連結して単一のを与えList[Boolean]ます。次に、そのリスト内の値がいくつあるかを知りたいと思いますtruecount述語を取り、コレクション内のいくつの値がその述語を満たすかを示します。たとえば、1から10までの偶数を数える1つの方法は次のとおりです。

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

ただし、あなたの場合、すでにブール値があるので、述語は何もする必要はありません。それは恒等関数である可能性がありますx => x。dhgがコメントで指摘しているように、ScalaのPredefオブジェクトは、これを、ここで使用しているという名前のメソッドとして提供identityします。rowsByColumns.flatten.count(x => x)ただし、それがより明確であることがわかった場合は、同じように簡単に書くことができます。

于 2012-04-28T16:41:54.170 に答える
3

より簡単なケースを見て、あなたの質問を調べることができます。

(0 to 1).map(x => if(x > 1) 1 else 0)  // fine
(0 to 1).map(if(_ > 1) 1 else 0)       // error

私たちが見るエラーは

<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.$greater(1))
              (0 to 1).map(if(_ > 1) 1 else 0)
                              ^

だから何が起こっているのかというと、Scalaは可能な限り狭い範囲で拡大_しているということです。(x$1) => x$1言い換えれば、それはやろうとしている:

(0 to 1).map(if((x) => x > 1) 1 else 0)

しかし、これは間違っています。

あなたの場合も同様です。この2つ_は、両方が同じスコープ内にあるものとしては扱われません。そのため、パラメーターは1つだけであると見なされます。2つ目_は、のスコープ内で展開されますがif、これは誤りです。それはあなたがこれをしていると思います:

row.foldLeft[Int](0)((x) => x + (if ((y) => y) 1 else 0))
于 2012-04-28T16:33:10.637 に答える
1

これについてさらに考えてみると、ifの評価式で必要な括弧を削除するようにScalaの構文を変更するというOderskyの提案(SIP 12 )が、元の誤ったステートメントを削除して元のエラーステートメントを再実装するように彼の「欠陥」を修正する可能性はありますか? ifからの括弧は正しく機能します

注(8月216日、4年後):決定は

将来のリリースに延期

このSIPは現在延期されており、2.10の現在のアクションがあります。

  • then(1)将来のキーワードステータスのために予約するために、識別子の「」を非推奨にします。
  • while() do(2)問題のある" "構文を廃止します。(" do" whileループには中括弧が必要です)。

もともと2011年に提案されたこの提案は、Scalaの構文をJavaやCのような言語から遠ざけるために、ifの構文の変更forとループを提案しました。 そのような変化は間違いなくより美しいかもしれませんが、委員会は利益よりも多くの問題を与えることに同意しました。while

555号を参照

于 2016-08-21T12:54:54.127 に答える