Избавляемся от петель в Scala - PullRequest
0 голосов
/ 11 октября 2018

Вот проблема, которая включает factorial.Для заданного числа, n, найдите ответ на следующий вопрос:

(1 / n!) * (1! + 2! + 3! + ... + n!)

Итеративное решение в Scala очень простое - достаточно простого цикла for.

object MyClass {

def fsolve(n: Int): Double = {
    var a: Double = 1
    var cum: Double = 1
    for (i <- n to 2 by -1) { 
        a = a * (1.0/i.toDouble)
        cum += a
    }
    scala.math.floor(cum*1000000) / 1000000
}

def main(args: Array[String]) {
    println(fsolve(7))     // answer 1.173214
}

}

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

object MyClass {

    def fsolve(n: Int) = {
        (n to 2 by -1).foldLeft(1.toDouble) (_*_)
        // what comes next????
    }

    def main(args: Array[String]) {
        println(fsolve(7))
    }
}

Какие-либо предложения или указатели на решение?

Ответы [ 3 ]

0 голосов
/ 11 октября 2018

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

def fsolve(n: Int): Double = {
  require(n > 0, "n must be positive")
  def f(n: Int): Long = (1 to n).fold(1)(_ * _)
  (1.0 / f(n)) * ((1 to n).map(f).sum)
}

В функции, которую я проверяю, происходит сбой при неверном вводе с require, я определяюфакториал (как f), а затем используйте его, просто записав функцию как можно ближе к исходному выражению, которое мы хотели реализовать:

(1.0 / f(n)) * ((1 to n).map(f).sum)

Если вы действительно хотите явно сложить, вы можете переписатьэто выражение выглядит следующим образом:

(1.0 / f(n)) * ((1 to n).map(f).fold(0L)(_ + _))

Также обратите внимание, что, поскольку все выполняемые вами операции (суммы и умножения) являются коммутативными, вы можете использовать fold вместо foldLeft: использование первого не делает 't предписывает порядок, в котором должна выполняться операция, позволяя конкретной реализации коллекции выполнять вычисления параллельно.

Вы можете поиграться с этим кодом здесь, в Scastie .

0 голосов
/ 11 октября 2018

Формула, которую вы предоставили, очень хорошо отображает функцию scanLeft.Он работает как комбинация foldLeft и map, выполняет операцию сгиба, но сохраняет каждое сгенерированное значение в списке вывода.Следующий код генерирует все факториалы от 1 до n, суммирует их, а затем делит на n!.Обратите внимание: выполняя одиночное деление с плавающей запятой в конце, а не на каждом промежуточном шаге, вы уменьшаете вероятность ошибок с плавающей запятой.

def fsolve(n: Int): Double =
{
  val factorials = (2 to n).scanLeft(1)((cum: Int, value: Int) => value*cum)
  scala.math.floor(factorials.reduce(_+_)/factorials.last.toDouble*1000000)/1000000
}
0 голосов
/ 11 октября 2018

Результат возвращается из foldLeft, например:

val cum = (n to 2 by -1).foldLeft(1.toDouble) (_*_)

Только в вашем случае функция должна отличаться, так как приведенный выше сгиб умножил бы все значения i вместе.Вы передадите значения cum и a для складывания:

def fsolve(n: Int): Double = {
  val (cum, _) = (n to 2 by -1).foldLeft(1.0, 1.0) { case ((sum, a),i) =>
    val newA = a * (1.0/i.toDouble)
    (sum + newA, newA)
  }
  scala.math.floor(cum*1000000) / 1000000
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...