2011-01-29 4 views
34

Я наблюдал Runar Bjarnason present Functional Programming for Beginners, а в 14:45 он определяет метод:Функции по сравнению с методами Scala

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

и функции:

val isEven = isDivisibleBy(2) 

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

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

def isEven = isDivisibleBy(2) 
+0

Просто думать об этом в эквивалентной Java (функции против методов делает хорошую работу, что Scala), и как она вписывается в время выполнения. Это хорошая причина для одного подхода к другому. Также обратите внимание, что область «def» важна, поскольку она не везде везде (это всего лишь «метод» - подробности реализации в сторону - на уровне класса). –

ответ

46

Под капотом существуют другие различия между функциями и методами. Как правило, простой метод генерирует меньше накладных расходов, чем функция (которая технически является объектом с методом 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 
} 
+1

Как насчет разницы между 'def' на уровне класса и vs' def' внутри метода? +1, так как это хороший ответ (хотя каждый раз применяется функция - например, в 'val' он будет вызываться - это просто деталь, что методы могут быть вызваны без явного parens), но было бы хорошо чтобы зафиксировать эти различия (и значение «метода»). –

+0

немой вопрос: что делает 'Boolean =' там? Я новичок в Scala. :) – DavidLin

+0

Означает ли это, что вызов функции isEven будет быстрее, может вызвать метод isEven? Это преимущество функций над методами? –

5

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

+0

Это не определение функции, но приложение метода с результатом функции: оно показывает, что функция (Int => Boolean) может быть сохранена/обработана как нормальный объект. 'val square = (x) => x * x' было бы определением, которое также показывает одно и то же. Тонкие нюансы между двумя (** методы, которые привязаны к объекту и допускают перегрузку и переопределение ** (в терминах Java) и ** функции, которые не являются методами **), там, где они становятся «интересными». –

+0

У меня смутное понимание того, что такое 'function definition', потому что это довольно сложно для google :-), поэтому я использую его интуитивно понятным способом. Как «квадрат квадрата ...» другой? Он также принимает аргумент и создает функцию, возвращающую объект, применяя метод '*' к 'x'. Я попытался внимательно прочитать вопрос и ответить на этот конкретный вопрос. «в этом случае» имеет решающее значение, вопрос заключается в упоминании человека, говорящего на презентации, и его точка зрения заключалась в том, чтобы показать другой способ «лечения» методов/функций, вопрос был не о нюансах между методом и функциональным объектом. – coubeatczech

+0

Метод, если артефакт Java (возможно, Scala имел бы их, если бы не часть Java) - объекты имеют * методы * (они определены с 'def' на уровне класса). Функции в Scala - это только особая форма объекта (см. FunctionN и т. Д.), Которые имеют метод «apply» ('def' внутри функции: * not methods * - фактически, они могут быть/реализованы как таковые, но это деталь реализации, поскольку это не отображается).Scala просто превращает * метод * в * функцию * (или сохраняя * функцию * в переменной) тривиально. Я не видел этого различия в любом ответе. –

3

Метод 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's syntax for currying).

+0

С currying кажется, что он немного медленнее, не знаю почему. – Debilski

13

Я хотел бы обратиться к другому пункту здесь. Это определяет 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

Насколько я знаю, 'val' не определяет метод. Может быть, вы имеете в виду, что по какой-либо причине он переводится в метод в java-байтовом коде (хотя я не понимаю, почему это было бы так). Я бы подумал, что это просто ссылка на объект функции. В конце концов вы можете просто передать ссылку, как с другими объектами. – herman

Смежные вопросы