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 ))
      }
    }
  }  | 
 
		
		
- 包含"dummy"的代码在my repl(scala 2.9.0.1)中运行。比如用prod (20)来称呼它。
 
- 在实际的代码示例中,((a+1) to max/a).filter(b => p(b) && b*a < max).map{ b => em(a,b,max) }将完成这个技巧。另外,第一个地图应该是平面图。
 
- 非常感谢。部分原因是,我的错误现在对我来说是显而易见的——过滤器...filter (p)中的布尔数组p使b在表达式中消失,而后面需要它,所以filter (b => p(b))是前进的道路。将过滤器与&& b*a < max结合也很清楚。如果我再搜索4个小时,我就找不到重复的b =>,我想我明天也找不到它,也不找这里。
 
- 如果你真的想,你可以第二次把它叫做x =>,而不是b =>。它只是需要一个名称的东西;它通过过滤器后恰好是相同的东西,所以我使用了相同的变量。
 
 
	  
你需要这样写:
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) | 
 
号
		
		
- 因此,我可以省略带大括号的分号,并使用下划线作为比命名虚拟对象更规范的方式来表示unused dummy,但我可以使用这两种方法彼此独立。
 
- @用户未知:是。
 
- 老实说,我认为dummy = print(a +"")更清楚——如果这是唯一的改变。其他人看到这行,他们会立即知道这是一个用于打印的虚拟变量赋值。当使用下划线时,他们可能会怀疑这是否是他们以前从未见过的普遍下划线的新用法。
 
- 我发现_版本被清除了。我想是个人品味的问题吧。
 
- S/清除/清除
 
 
	  
从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   | 
 
。
		
		
- tap也可作为scala 2.12的后端端口:github.com/bigwheel/util-backports
 
 
	  
 
把一个有副作用的语句放在一个内以供理解(或者实际上放在任何函数的中间)似乎不是一个好的风格,除了调试之外,在这种情况下,您称它为什么并不重要("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循环如何被转换为map/flatmap,但是我对它的工作方式有了错误的印象,并且只注意到使用upper方法时我的印象是错误的。所有的a都打印出来了,但是花了几分钟的时间才执行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 )
  }
}  | 
 
这也使得添加打印语句变得更加容易。
		
		
- 好吧,真正的代码在A、B上有一个额外的过滤器,它不仅产生C,而且产生一个方法结果,这取决于(A、B、M),这意味着我需要一组额外的花括号来捕获B,但不知何故我在正确包装它的过程中迷失了方向。在相反的方向上,我可以在我的外部for中添加花括号,然后在没有虚拟对象的情况下使用print,并将结果变平,但这也变得更难理解,imho。但是对于类似的情况,我必须记住使用map/flatmap方法的花括号。
 
- @用户未知-(2 to m/(2*3)).filter(f).flatMap { a =>和map(b => g(a,b,m))将执行此操作,其中f是a上的过滤器,g是a、b、m的函数(除非您的意思是不过滤函数的结果,在这种情况下,您需要在内部循环中交换过滤器和映射的顺序)。
 
- 谢谢。我把我的代码放到这个问题中,只是出于好奇,因为我观察到我的b的一代已经针对(a*b>m)进行了消毒,所以不需要最后一次测试,而且我无法毫无问题地执行您的模式。但更长的结构不知何故阻塞了我的大脑。也许你喜欢解决这个问题。:)