在 Scala 中进行 println for-comprehension

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7413185/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 03:27:37  来源:igfitidea点击:

println in scala for-comprehension

scalavariable-assignmentprintlnfor-comprehension

提问by user unknown

In a for-comprehension, I can't just put a print statement:

在理解中,我不能只放一个打印语句:

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
}

but I can circumvent it easily with a dummy assignment:

但我可以通过一个虚拟任务轻松绕过它:

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
}

Being a side effect, and only used (so far) in code under development, is there a better ad hoc solution?

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

Is there a serious problem why I shouldn't use it, beside being a side effect?

除了副作用之外,还有一个严重的问题为什么我不应该使用它?

update showing the real code, where adapting one solution is harder than expected:

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

From the discussion with Rex Kerr, the necessity has risen to show the original code, which is a bit more complicated, but did not seem to be relevant for the question (2x .filter, calling a method in the end), but when I tried to apply Rex' pattern to it I failed, so I post it here:

从与 Rex Kerr 的讨论来看,显示原代码的必要性上升了,这有点复杂,但似乎与问题无关(2x .filter,最后调用了一个方法),但是当我试图将 Rex' 模式应用于它我失败了,所以我把它贴在这里:

  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)) }

Here is my attempt -- (b * a).filter is wrong, because the result is an int, not a filterable collection of ints:

这是我的尝试 -- (b * a).filter 是错误的,因为结果是一个整数,而不是一个可过滤的整数集合:

  // 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))
      }
    }
  }

Part II belongs to the comments, but can't be read, if written there - maybe I delete it in the end. Please excuse.

第二部分属于评论,但不能看,如果写在那里——也许我最后删除了它。请包涵。

Ok - here is Rex last answer in code layout:

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

  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))
      }
    }
  }

回答by missingfaktor

This is how you need to write it:

这是您需要编写的方式:

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)

回答by Xavier Guihot

Starting Scala 2.13, the chaining operation tap, has been included in the standard library, and can be used with minimum intrusiveness wherever we need to print some intermediate state of a pipeline:

开始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)


The tapchaining operation applies a side effect (in this case println) on a value (in this case a) while returning the value (a) untouched:

tap链接操作适用的副作用(在这种情况下println上的值)(在这种情况下a),而在返回值(a)不变:

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

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



It's very convenient when debugging as you can use a bunch of taps without having to modify the code:

调试的时候很方便,tap不用修改代码就可以用一堆s:

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

回答by Rex Kerr

I generally find that style of coding rather difficult to follow, since loops and intermediate results and such get all mixed in with each other. I would, instead of a for loop, write something like

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

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

This also makes adding print statements and such easier.

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

回答by Luigi Plinge

It doesn't seem like good style to put a side-effecting statement within a for-comprehension (or indeed in the middle of any function), execept for debugging in which case it doesn't really matter what you call it ("debug" seems like a good name).

将副作用语句放在 for-comprehension 中(或实际上放在任何函数的中间)似乎不是一种好的风格,除了调试,在这种情况下,你怎么称呼它并不重要(“调试" 似乎是个好名字)。

If you really need to, I think you'd be better separating your concerns somewhat by assigning an intermediate val, e.g. (your original laid out more nicely):

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

  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) 
  }

becomes

变成

  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)
    }
  }