Идиоматическое решение Scala императивного кода - PullRequest
9 голосов
/ 30 декабря 2011

Каковы некоторые идеи для выражения этой функции в «идиоматическом» Scala.Или, точнее, есть ли способ удалить локальные переменные, не жертвуя удобочитаемостью?

def solve(threshold: Int)(f: Int => Int): Int = {
  var sum = 0
  var curr = 0
  while(sum < threshold) {
   sum += f(curr)
   curr += 1
  }
  curr
}

Единственное, что я мог придумать, было это, но, на мой взгляд, он длиннее и менее читабелен.

def solve2(threshold: Int)(f: Int => Int): Int = {
  val resultIterator = Iterator.iterate (0, 0) { case (curr, sum) =>
    (curr + 1, sum + f(curr))
  }
  (resultIterator find (_._2 >= threshold)).get._1
}

Ответы [ 5 ]

13 голосов
/ 31 декабря 2011
def solve(threshold: Int)(f: Int => Int): Int = {
  Iterator.from(0).map(f).scanLeft(0)(_ + _).indexWhere(threshold <=)
}

На мой взгляд, версия цикла гораздо понятнее.

10 голосов
/ 31 декабря 2011

Самый прямой подход - превратить цикл while во вложенную хвостовую рекурсивную функцию.

def solve(threshold: Int)(f: Int => Int): Int = {
    def solveLoop(sum: Int, curr: Int): Int = if (sum < threshold) {
        solveLoop(sum + f(curr), curr + 1)
    } else {
        curr
    }
    solveLoop(0,0)
}

Это стандартный «функциональный» способ зацикливания.

7 голосов
/ 31 декабря 2011

Вы могли бы

def solve(threshold: Int, i: Int = 0)(f: Int => Int) = {
  if (threshold <= 0) i else solve(threshold - f(i), i+1)(f)
}

, но я не уверен, что это на самом деле яснее.Обратите внимание, что на самом деле это больше символов, чем компактная версия цикла while:

def solve(threshold: Int)(f: Int => Int) = {
  var s,i = 0; while (s < threshold) { s += f(i); i += 1 }; i
}

Циклы с изменяемыми переменными не всегда плохи, «идиоматичны» или нет.Просто сохраняйте изменчивое состояние, которое безопасно содержится внутри функции, и все, что кто-либо еще видит, - это функция без сохранения состояния.

Кстати, хотя sum - разумное имя переменной, curr сомнительно.Что не так с i?Он широко используется в качестве индексной переменной, и в любом случае наличие переменной вообще является неприятностью;Дело в том, что вы берете что-то и увеличиваете это, что бы это ни было, на один шаг каждый раз, а затем возвращаете это.Именно этот поток логики, а не имя, говорит вам (и другим), для чего он нужен.

4 голосов
/ 31 декабря 2011

Вот как я бы сделал это в Haskell:

solve threshold f = snd $ until test step (0, 0)
  where test (sum, i) = sum >= threshold
        step (sum, i) = (sum + f i, succ i)

Это ясно отмечает test, step и начальные значения, точно так же, как императивная версия.Я не уверен, что у scala есть until в библиотеках где-то, но определить тривиально:

def until[A](test: A => Boolean)(f: A => A)(v: A): A = {
  if (test(v)) {
    v
  } else {
    until(test)(f)(f(v))
  }
}

def solve(threshold: Int)(f: Int => Int): Int = {
  def test = (sum: Int, i: Int) => sum >= threshold
  def step = (sum: Int, i: Int) => (sum + f(i), i + 1)
  until(test.tupled)(step.tupled)((0, 0))._2
}
1 голос
/ 31 декабря 2011

Мне всегда интересно, когда люди говорят о «идиоматической» скале.Потому что, на мой взгляд, у каждого свое восприятие идиоматизма.Если вы ищете функциональное решение, я хотел бы предложить вам взглянуть на «сущность шаблона итератора».На самом деле в scala есть очень хороший пост на эту тему, посмотрите здесь: http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...