Анонимный Scala синтаксис функции

Я больше узнаю о Scala, и я немного затрудняюсь понять пример анонимных функций в http://www.scala-lang.org/node/135. Я скопировал весь блок кода ниже:

object CurryTest extends Application {
    def filter(xs: List[Int], p: Int => Boolean): List[Int] =
        if (xs.isEmpty) xs
        else if (p(xs.head)) xs.head :: filter(xs.tail, p)
        else filter(xs.tail, p)

    def modN(n: Int)(x: Int) = ((x % n) == 0)

    val nums = List(1, 2, 3, 4, 5, 6, 7, 8)
    println(filter(nums, modN(2)))
    println(filter(nums, modN(3)))
}

Я запутался с применением функции modN

def modN(n: Int)(x: Int) = ((x % n) == 0)

В этом примере он вызывается с одним аргументом

modN(2) and modN(3)

Что означает синтаксис modN (n: Int) (x: Int)?

Поскольку он вызывает один аргумент, я предполагаю, что это не оба аргумента, но я не могу понять, как значения из nums используются функцией mod.

Ответ 1

Это забавная вещь в функциональном программировании под названием currying. В основном Моисей Шонфинкель и последний Хаскелл Карри (Schonfinkeling звучат странно, хотя...) придумали идею о том, что вызов функции из нескольких аргументов, например f(x,y), совпадает с цепочкой вызовов {g(x)}(y) или g(x)(y), где g - это функция, которая выдает другую функцию.

В качестве примера возьмем функцию f(x: Int, y: Int) = x + y. Вызов f(2,3) вызвал бы 5, как и ожидалось. Но что происходит, когда мы выполняем эту функцию - переопределяем ее как f(x:Int)(y: Int) и называем ее f(2)(3). Первый вызов f(2) создает функцию с целым числом y и добавляет к ней 2 → поэтому f(2) имеет тип Int => Int и эквивалентен функции g(y) = 2 + y. Второй вызов f(2)(3) вызывает вновь созданную функцию g с аргументом 3, поэтому оценивая 5, как и ожидалось.

Еще один способ его просмотра - переступить редукцию (функциональные программисты называют это бета-редукцию - он похож на функциональный способ поэтапного перехода по строке) вызова f(2)(3) (обратите внимание, что это не действительно действительно Scala).

f(2)(3)         // Same as x => {y => x + y}
 | 
{y => 2 + y}(3) // The x in f gets replaced by 2
       |
     2 + 3      // The y gets replaced by 3
       |
       5

Итак, после всего этого разговора f(x)(y) можно рассматривать как просто следующее lambda выражение (x: Int) => {(y: Int) => x + y} - которое действительно Scala.

Я надеюсь, что все это имеет смысл - я попытался дать немного информации о том, почему вызов modN(3) имеет смысл:)

Ответ 2

Вы частично используете функцию ModN. Приложение с частичной функцией является одной из основных функций функциональных языков. Для получения дополнительной информации ознакомьтесь с этими статьями Currying и Pointfree.

Ответ 3

В этом примере modN возвращает функцию, которая модифицирует конкретный N. Это избавляет вас от необходимости делать это:

def mod2(x:Int): Boolean = (x%2) == 0
def mod3(x:Int): Boolean = (x%3) == 0

Две пары парсеров разделяют, где вы можете прекратить передавать аргументы методу. Конечно, вы также можете просто использовать местозаполнитель для достижения того же самого, даже если у метода есть только один список аргументов.

def modN(n: Int, x: Int): Boolean = (x % n) == 0

val nums = List(1, 2, 3, 4, 5)
println(nums.filter(modN(2, _)))
println(nums.filter(modN(3, _)))