Функции против методов в Scala

Я смотрю, как Runar Bjarnason представил функциональное программирование для начинающих, и в 14:45 он определяет метод:

def isDivisibleBy(k: Int): Int => Boolean = i => i % k == 0

и функция:

val isEven = isDivisibleBy(2)

Каковы плюсы и минусы определения isEven как функция, а не метод?

Я прочитал Scala Functions vs Methods, а также Difference между методом и функцией в Scala, и я понимаю семантические различия, но мне интересно, есть ли в этом случае более глубокая причина, почему функция может или не предпочтительнее использовать метод:

def isEven = isDivisibleBy(2)

Ответы

Ответ 1

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

Однако, если вы попытаетесь не заботиться об этих различиях и подумайте о def, val и var в качестве полей с различной семантикой, тогда простое значение def будет оцениваться каждый раз, когда он будет вызван, а val оценивается только один раз.

Итак, a val isEven = isDivisibleBy(2) должен вызывать isDivisibleBy(2) во время своего определения и назначать результат isDivisibleBy(2). Например. он заменяет k в

def isDivisibleBy(k: Int): Int => Boolean = i => i % k == 0

с 2 и присваивает результат окончательного выражения (в этом случае есть только одно выражение):

val isEven: Int => Boolean = i => i % 2 == 0

def isEven, с другой стороны, не дает такой оценки и приводит к вызову isDivisibleBy (2) каждый раз.

Это означает, что позже, когда вы выполняете код, isEven(11) генерируется в случае val

11 % 2 == 0

а в случае def у вас будет

isDivisibleBy(2)(11)

и только после оценки isDivisibleBy вы получите результат.

Вы можете добавить код отладки в isDivisibleBy, чтобы увидеть разницу:

def isDivisibleBy(k: Int): Int => Boolean = {
  println("evaluating isDivisibleBy")
  i => i % k == 0
}

Ответ 2

Я хотел бы остановиться на другом вопросе. Это определяет isEven как метод:

def isEven = isDivisibleBy(2)

И это определяет isEven как метод:

val isEven = isDivisibleBy(2)

В обоих случаях isEven - это метод, который при вызове возвращает функцию.

В первом случае isDivisible(2) вызывается каждый раз, когда вызывается isEven. Например, это вызывает isDivisible(2) три раза:

def isEven = isDivisibleBy(2)
List(1,2,3).filter(isEven)

Во втором случае isDivisible(2) вызывается один раз (во время построения или когда эта строка в определении выполняется), и это значение извлекается каждый раз, когда вызывается isEven. В следующем примере вызов isDivisible(2) выполняется только один раз:

val isEven = isDivisibleBy(2)
List(1,2,3).filter(isEven)

Ответ 3

Я думаю, что основным профиом определения функции isEven как val является показать аудитории, что функция может быть определена таким образом. Тогда ясно, что функция - это просто объект, как и все остальное в scala. Но в мире без демонстрации программирования нет необходимости писать функции как val s.

Ответ 4

Метод def isDivisibleBy(k: Int): Int => Boolean возвращает функцию, которая принимает параметр Int (i) в качестве параметра и возвращает логическое значение (i % k == 0).

val isEven = isDivisibleBy(2) С другой стороны, это поле, в которое хранится функция, возвращаемая isDivisibleBy(2). Если вы используете def вместо val, тогда метод isDivisibleBy будет вызываться каждый раз, когда вызывается метод isEven, но теперь он вызывает только один раз, и результат сохраняется в поле.

Вы можете добиться того же результата, написав def isEven(i: Int): Boolean = i % 2 == 0

Я думаю, что точка примера состоит в том, что вы можете иметь функции, которые возвращают другие функции, и вы можете хранить функции как объекты, а затем вызывать их так, как если бы они были традиционно определенными методами. Вышеприведенный код также очень похож на currying, так что это может быть и одно, продемонстрированное на примере (хотя оно не использует Scala синтаксис для currying).