println в Scala для понимания - PullRequest
       31

println в Scala для понимания

29 голосов
/ 14 сентября 2011

В целях понимания я не могу просто написать заявление для печати:

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
}

Являясь побочным эффектом и использовавшимся (пока) только в разрабатываемом коде, есть ли лучшее специальное решение?

Есть ли серьезная проблема, почему я не должен использовать это, помимо побочного эффекта?

обновление, показывающее реальный код, где адаптация одного решения сложнее, чем ожидалось:

Из разговора с Рексом Керром возникла необходимость показать оригинальный код, который немного сложнее, но, похоже, не имеет отношения к вопросу (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))
      }
    }
  }

Ответы [ 4 ]

42 голосов
/ 14 сентября 2011

Вот как это нужно написать:

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)
4 голосов
/ 17 марта 2019

Начиная с 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


Это очень удобно при отладке, так как вы можете использовать кучу 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
2 голосов
/ 14 сентября 2011

Не похоже на хороший стиль, чтобы поместить побочный эффект в выражение для понимания (или даже в середину любой функции), за исключением отладки, в этом случае не имеет значения, как вы его называете(«отладка» выглядит как хорошее имя).

Если вам действительно нужно, я думаю, что вам лучше несколько разделить свои проблемы, назначив промежуточный val, например (ваш оригинал выложен более красиво):

  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)
    }
  }
2 голосов
/ 14 сентября 2011

Я обычно нахожу, что такой стиль кодирования довольно сложен, поскольку циклы и промежуточные результаты и тому подобное смешиваются друг с другом. Я бы вместо цикла for написал что-то вроде

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

Это также упрощает добавление выписок и т. П.

...