关于变量赋值:scala for-comprehension中的println

println in scala for-comprehension

为了理解,我不能只写一个印刷体声明:

1
2
3
4
5
6
7
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
}

但我可以很容易地通过一个虚拟的任务来规避它:

1
2
3
4
5
6
7
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
}

作为一个副作用,并且只在开发中的代码中使用(到目前为止),有没有更好的临时解决方案?

除了副作用,还有什么严重的问题我不应该使用它吗?

显示真实代码的更新,其中调整一个解决方案比预期困难:

通过与Rex-Kerr的讨论,有必要显示原始代码,这有点复杂,但似乎与问题无关(2x.filter,最终调用了一个方法),但当我试图将Rex的模式应用到它时,我失败了,所以我将它发布在这里:

1
2
3
4
5
6
7
8
  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)。筛选错误,因为结果是一个int,而不是可筛选的int集合:

1
2
3
4
5
6
7
8
9
  // 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))
      }
    }
  }

第二部分属于评论,但不能阅读,如果写在那里-也许我会在最后删除它。请原谅。

好的-这是rex在代码布局中的最后一个答案:

1
2
3
4
5
6
7
8
  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))
      }
    }
  }


你需要这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)


Scala 2.13开始,链接操作tap已包含在标准库中,并且可以在需要打印管道的某些中间状态时以最小的侵入性使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

调试时非常方便,因为您可以使用一堆tap而无需修改代码:

1
2
3
4
5
6
7
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


把一个有副作用的语句放在一个内以供理解(或者实际上放在任何函数的中间)似乎不是一个好的风格,除了调试之外,在这种情况下,您称它为什么并不重要("debug"似乎是一个好名字)。

如果您真的需要,我认为您最好通过分配一个中间值来稍微分离您的关注点,例如(您的原始布局更漂亮):

1
2
3
4
5
6
7
8
  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)
  }

变成

1
2
3
4
5
6
7
8
9
10
11
12
  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)
    }
  }


我通常发现这种编码方式很难遵循,因为循环和中间结果等都是相互混合的。我会写一些类似的东西,而不是for循环

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

这也使得添加打印语句变得更加容易。