Функции против методов в 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).