web-dev-qa-db-ja.com

println in scala for-comprehension

理解のために、私は単に印刷ステートメントを置くことはできません:

def prod (m: Int) = {
  for (a <- 2 to m/(2*3);
    print (a + "  ");
    b <- (a+1) to m/a;
    c = (a*b) 
    if (c < m)) yield c
}

しかし、ダミーの割り当てで簡単に回避できます。

def prod (m: Int) = {
  for (a <- 2 to m/(2*3);
    dummy = print (a + "  ");
    b <- (a+1) to m/a;
    c = (a*b) 
    if (c < m)) yield c
}

副作用であり、(これまでのところ)開発中のコードでのみ使用されているので、より良いアドホックソリューションはありますか?

副作用以外に、なぜ使わないのかという深刻な問題はありますか?

実際のコードを示す更新。1つのソリューションの適応が予想よりも難しい場合:

Rex Kerrとの話し合いから、元のコードを表示する必要性が高まっています。これは少し複雑ですが、質問(2x .filter、最後にメソッドを呼び出す)には関連していないようですが、レックスのパターンを適用しようとして失敗したので、ここに投稿します。

  def prod (p: Array[Boolean], max: Int) = {
    for (a <- (2 to max/(2*3)).
        filter (p);
      dummy = print (a + "  ");
      b <- (((a+1) to max/a).
         filter (p));
      if (a*b <= max)) 
        yield (em (a, b, max)) }

これが私の試みです-(b * a).filterは間違っています。結果はintであり、フィルター可能なintのコレクションではないからです。

  // wrong: 
  def prod (p: Array[Boolean], max: Int) = {
    (2 to max/(2*3)).filter (p).flatMap { a =>
      print (a + " ")
      ((a+1) to max/a).filter (p). map { b => 
        (b * a).filter (_ <= max).map (em (a, b, max))
      }
    }
  }

パートIIはコメントに属していますが、そこに書かれていると読むことができません。最後に削除するかもしれません。失礼します。

わかりました-これがコードレイアウトにおけるレックスの最後の答えです:

  def prod (p: Array[Boolean], max: Int) = {
    (2 to max/(2*3)).filter (p).flatMap { a =>
      print (a + " ")
      ((a+1) to max/a).filter (b => p (b) 
        && b * a < max).map { b => (m (a, b, max))
      }
    }
  }
29
user unknown

これはあなたがそれを書く必要がある方法です:

scala> def prod(m: Int) = {
     |   for {
     |     a <- 2 to m / (2 * 3)
     |     _ = print(a + " ")
     |     b <- (a + 1) to (m / a)
     |     c = a * b
     |     if c < m
     |   } yield c
     | }
prod: (m: Int)scala.collection.immutable.IndexedSeq[Int]

scala> prod(20)
2 3 res159: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 8, 10, 12, 14
, 16, 18, 12, 15, 18)
42
missingfaktor

開始Scala 2.13、連鎖操作 tap は標準ライブラリに含まれており、パイプラインの中間状態を出力する必要がある場合は、最小限の煩わしさで使用できます。

import util.chaining._

def prod(m: Int) =
  for {
    a <- 2 to m / (2 * 3)
    b <- (a + 1) to (m / a.tap(println)) // <- a.tap(println)
    c =  a * b
    if c < m
 } yield c

prod(20)
// 2
// 3
// res0: IndexedSeq[Int] = Vector(6, 8, 10, 12, 14, 16, 18, 12, 15, 18)

tap チェーン操作は、値(この場合はprintln)に副作用(この場合はa)を適用し、値( a)手つかず:

def tap [U](f:(A)=> U):A


コードを変更せずに一連のtapsを使用できるため、デバッグ時に非常に便利です。

def prod(m: Int) =
  for {
    a <- (2 to m.tap(println) / (2 * 3)).tap(println)
    b <- (a + 1) to (m / a.tap(println))
    c = (a * b).tap(println)
    if c < m
 } yield c
4
Xavier Guihot

ループや中間結果などがすべて互いに混ざり合うため、一般的に、コーディングのスタイルを理解するのはかなり難しいと思います。 forループの代わりに、次のように記述します。

def prod(m: Int) = {
  (2 to m/(2*3)).flatMap { a =>
    print(a + " ")
    ((a+1) to m/a).map(_ * a).filter(_ < m)
  }
}

これにより、printステートメントなどの追加も簡単になります。

2
Rex Kerr

副作用のあるステートメントを理解のために(または実際には関数の途中に)置くのは良いスタイルではないようですが、デバッグを除いて、それを何と呼ぶか​​は実際には重要ではありません(「デバッグ」 「いい名前のようです)。

本当に必要な場合は、中間の値を割り当てることで、懸念事項をある程度分離したほうがよいと思います。 (あなたのオリジナルはよりうまくレイアウトされています):

  def prod (p: Array[Boolean], max: Int) = {
    for {
      a <- (2 to max / (2 * 3)) filter p
      debug = print (a + "  ")
      b <- ((a + 1) to max / a) filter p
      if a * b <= max
    } yield em(a, b, max) 
  }

になります

  def prod2 (p: Array[Boolean], max: Int) = {
    val as = (2 to max / (2 * 3)) filter p

    for(a <- as) print(a + "  ")

    as flatMap {a => 
      for {
        b <- ((a + 1) to max / a) filter p
        if a * b <= max
      } yield em(a, b, max)
    }
  }