Ответ 1
def IWantToDoSomethingSimilar(N: Int) =
for {
n <- 0 until N
if n % 2 != 0 || { println("skip even number " + n); false }
} yield n
Использование filter
вместо выражения for
было бы немного проще, хотя.
В языке Scala я хочу написать функцию, которая дает нечетные числа в заданном диапазоне. Функция печатает некоторый журнал при повторении четных чисел. Первая версия функции:
def getOdds(N: Int): Traversable[Int] = {
val list = new mutable.MutableList[Int]
for (n <- 0 until N) {
if (n % 2 == 1) {
list += n
} else {
println("skip even number " + n)
}
}
return list
}
Если я опускаю журналы печати, реализация становится очень простой:
def getOddsWithoutPrint(N: Int) =
for (n <- 0 until N if (n % 2 == 1)) yield n
Однако я не хочу пропускать часть регистрации. Как переписать первую версию более компактно? Было бы здорово, если бы его можно было переписать аналогично этому:
def IWantToDoSomethingSimilar(N: Int) =
for (n <- 0 until N) if (n % 2 == 1) yield n else println("skip even number " + n)
def IWantToDoSomethingSimilar(N: Int) =
for {
n <- 0 until N
if n % 2 != 0 || { println("skip even number " + n); false }
} yield n
Использование filter
вместо выражения for
было бы немного проще, хотя.
Я хочу сохранить последовательность ваших traitement (обработка коэффициентов и выравнивания по порядку, а не отдельно), вы можете использовать что-то вроде этого (отредактировано):
def IWantToDoSomethingSimilar(N: Int) =
(for (n <- (0 until N)) yield {
if (n % 2 == 1) {
Option(n)
} else {
println("skip even number " + n)
None
}
// Flatten transforms the Seq[Option[Int]] into Seq[Int]
}).flatten
EDIT, следуя той же концепции, более короткое решение:
def IWantToDoSomethingSimilar(N: Int) =
(0 until N) map {
case n if n % 2 == 0 => println("skip even number "+ n)
case n => n
} collect {case i:Int => i}
Если вы захотите разобраться в функциональном подходе, то начнется что-то вроде следующего:
Сначала некоторые общие определения:
// use scalaz 7
import scalaz._, Scalaz._
// transforms a function returning either E or B into a
// function returning an optional B and optionally writing a log of type E
def logged[A, E, B, F[_]](f: A => E \/ B)(
implicit FM: Monoid[F[E]], FP: Pointed[F]): (A => Writer[F[E], Option[B]]) =
(a: A) => f(a).fold(
e => Writer(FP.point(e), None),
b => Writer(FM.zero, Some(b)))
// helper for fixing the log storage format to List
def listLogged[A, E, B](f: A => E \/ B) = logged[A, E, B, List](f)
// shorthand for a String logger with List storage
type W[+A] = Writer[List[String], A]
Теперь вам нужно только написать свою функцию фильтрации:
def keepOdd(n: Int): String \/ Int =
if (n % 2 == 1) \/.right(n) else \/.left(n + " was even")
Вы можете попробовать его мгновенно:
scala> List(5, 6) map(keepOdd)
res0: List[scalaz.\/[String,Int]] = List(\/-(5), -\/(6 was even))
Затем вы можете использовать функцию traverse
, чтобы применить свою функцию к списку входов и собрать как записанные журналы, так и результаты:
scala> val x = List(5, 6).traverse[W, Option[Int]](listLogged(keepOdd))
x: W[List[Option[Int]]] = [email protected]
// unwrap the results
scala> x.run
res11: (List[String], List[Option[Int]]) = (List(6 was even),List(Some(5), None))
// we may even drop the None-s from the output
scala> val (logs, results) = x.map(_.flatten).run
logs: List[String] = List(6 was even)
results: List[Int] = List(5)
Я не думаю, что это можно сделать легко с пониманием. Но вы можете использовать раздел.
def getOffs(N:Int) = {
val (evens, odds) = 0 until N partition { x => x % 2 == 0 }
evens foreach { x => println("skipping " + x) }
odds
}
РЕДАКТИРОВАТЬ. Чтобы избежать печати сообщений журнала после завершения разбиения, вы можете изменить первую строку метода следующим образом:
val (evens, odds) = (0 until N).view.partition { x => x % 2 == 0 }