23

valScalaでは、割り当てのある多くの小さな式よりも大きな連鎖式を書くことを好む傾向があります。私の会社では、このタイプのコードのスタイルを進化させてきました。これは完全に工夫された例です(アイデアは、連鎖した呼び出しがたくさんある式を表示することです):

import scala.util.Random
val table = (1 to 10) map { (Random.nextInt(100), _) } toMap

def foo: List[Int] =
  (1 to 100)
    .view
    .map { _ + 3 }
    .filter { _ > 10 }
    .flatMap { table.get }
    .take(3)
    .toList

DanielSpiewakのScalaスタイルガイド(pdf)は、私が一般的に気に入っているものですが、複数行の式については説明していませんが、連鎖メソッド呼び出しの先頭のドット表記が正しくない可能性があることを示唆しています(ドキュメント:メソッド呼び出し/高階関数を参照)。このように直接。

上記の関数を書くための別の、より受け入れられた/慣用的な方法はありfooますか?

更新:2011年6月28日

以下にたくさんの素晴らしい答えと議論があります。100%「この方法でやらなければならない」という答えはないようですので、現在最も人気のある回答を投票で受け入れます。これは現在、コンプリヘンションベースです。個人的には、今のところ先頭のドット表記に固執し、それに伴うリスクを受け入れるつもりだと思います。

4

6 に答える 6

16

この例は少し非現実的ですが、複雑な表現の場合、理解を使用する方がはるかにクリーンなことがよくあります。

def foo = {
  val results = for {
    x <- (1 to 100).view
    y = x + 3 if y > 10
    z <- table get y
  } yield z
  (results take 3).toList
}

ここでの他の利点は、計算の中間段階に名前を付けて、より自己文書化できることです。

ただし、簡潔さが目標の場合は、これを簡単にワンライナーにすることができます(ポイントフリースタイルがここで役立ちます)。

def foo = (1 to 100).view.map{3+}.filter{10<}.flatMap{table.get}.take(3).toList
//or
def foo = ((1 to 100).view map {3+} filter {10<} flatMap {table.get} take 3).toList

そして、いつものように、可能な場合はアルゴリズムを最適化します。

def foo = ((1 to 100).view map {3+} filter {10<} flatMap {table.get} take 3).toList
def foo = ((4 to 103).view filter {10<} flatMap {table.get} take 3).toList
def foo = ((11 to 103).view flatMap {table.get} take 3).toList
于 2011-06-24T19:44:10.517 に答える
11

式全体を括弧のセットにラップして、物事をグループ化し、可能であればドットを避けます。

def foo: List[Int] =
  ( (1 to 100).view
    map { _ + 3 }
    filter { _ > 10 }
    flatMap { table.get }
    take(3)
    toList )
于 2011-06-24T19:58:34.697 に答える
6

これが即興でそれを行う方法です。間違いはありません。

(specMember
  setInfo   subst(env, specMember.info.asSeenFrom(owner.thisType, sym.owner))
  setFlag   (SPECIALIZED)
  resetFlag (DEFERRED | CASEACCESSOR | ACCESSOR | LAZY)
)

本物のコンパイラソース!

于 2011-06-27T09:11:59.590 に答える
5

私はたくさんのvalsを好む:

def foo = {
  val range = (1 to 100).view
  val mappedRange = range map { _+3 }
  val importantValues = mappedRange filter { _ > 10 } flatMap { table.get }
  (importantValues take 3).toList
}

コードで何を目的としているのかわからないため、valsにはランダムな名前を選択しました。val他の言及された解決策の代わりにsを選択することには大きな利点があります:

あなたのコードが何をするかは明らかです。あなたの例と他のほとんどの回答で言及されている解決策では、誰もそれが何をしているのか一目でわかりません。1つの式に含まれる情報が多すぎます。@Kevinによって言及されたfor-expressionでのみ、名前を伝えることを選択することは可能ですが、私はそれらが好きではありません。理由は次のとおりです。

  1. より多くのコード行が必要です
  2. 宣言された値とパターンが一致するため、速度は遅くなります(これについてはここで説明しました)。
  3. 私の意見ですが、見た目は醜いと思います
于 2011-06-25T09:49:39.563 に答える
2

私のルール:式が1行(80〜120文字)に収まる場合は、1行のままにして、可能な限りドットを省略します。

def foo: List[Int] = 
   (1 to 100).view map { _ + 3 } filter { _ > 10 } flatMap table.get take 3 toList

Kevinが指摘したように、ポイントフリースタイルは簡潔さを改善する可能性があります(ただし、それに慣れていない開発者にとっては読みやすさを損なう可能性があります)。

def foo: List[Int] = 
   (1 to 100).view map{3+} filter{10<} flatMap table.get take 3 toList

長さのために式を複数の行に分割する必要がある場合は、先頭のドット表記を完全に使用できます。この表記を使用するもう1つの理由は、操作に個別のコメントが必要な場合です。式の長さや個々の操作にコメントを付ける必要があるために、式を複数行に広げる必要がある場合は、式全体を括弧で囲むのが最適です(Alex Boisvertが示唆しているように。このような状況では、各(論理)操作を続行する必要があります。独自の行(つまり、複数の連続する操作を1つのコメントで簡潔に説明できる場合を除いて、各操作は1行になります):

def foo: List[Int] = 
   ( (1 to 100).view
     map { _ + 3 }
     filter { _ > 10 }
     flatMap table.get
     take 3
     toList )   

この手法は、先頭のドット表記を使用したり、式の最後で0引数のメソッドを呼び出したりするときに発生する可能性のあるセミコロン推論の問題を回避します。

于 2011-06-24T19:36:18.700 に答える
1

map私は通常、やなどにドットを使用しないようにしてfilterいます。だから私はおそらく次のようにそれを書くでしょう:

def foo: List[Int] =
  (1 to 100).view map { x =>
    x + 3 } filter { x =>
    x > 10 } flatMap { table.get } take(3) toList

先頭のドット表記は非常に読みやすくなっています。私はそれを使い始めるかもしれません。

于 2011-06-24T18:15:24.310 に答える