2016-05-28 4 views
3
def fun(f: Int => Unit) { 
    f(10) 
    f(20) 
} 
println("method 1 call:") 
fun(i => {println("hi"); println(i)}) 
println("method 2 call:") 
fun{println("hi"); println(_)} 

Выход:анонимная функция в качестве параметра

E:\test\scala>scala i.scala 
method 1 call: 
hi 
10 
hi 
20 
method 2 call: 
hi 
10 
20 

Я думаю i => {println("hi"); println(i)} и println("hi"); println(_) одинаковы. Поскольку у нас есть один параметр, и параметр используется один раз, мы можем использовать _ для упрощения кода.

Тогда почему метод 2 просто печатает «привет» один раз? (Это означает: если я хочу использовать _ для простого вызова, то содержимое справа от => может иметь только одно выражение, если оно имеет более одного, например println («hi»); println (i); , мы не можем использовать _ заменить

ответ

1

Во-первых, вы должны знать, что в scala { block; of; code } это выражение, которое оценивается независимо от того, что оценивает последнее выражение внутри.

Когда вы говорите:

fun(i => { println("hi"); println(i) }) 

создать анонимную функцию, которая содержит корпус 2 выражения, как возвращение () и оба вычисляются, когда функция вызывается, все, как и ожидалось.

Но когда вы говорите

fun({println("hi"); println(_)}) 

Вы передаете в блоке, не анонимную функцию. Как пояснил sepp2k это расширяется

{ println("hi"); x => println(x) } 

Таким образом, вы передаете блок fun, этот блок оценивается, прежде чем он будет принят. Итак, первый println("hi") происходит, он печатается только один раз, когда блок оценивается один раз, а затем оценивается этот x => println(x), который является функцией Int => Unit, которая печатает свой аргумент. Это и только это (в качестве последнего выражения передается fun. Именно поэтому каждый раз, когда вы вызываете fun, он просто печатает аргумент дважды.

Чтобы увидеть дальше, как блок может работать, вы можете посмотреть на этом примере, что делает более в блоке

fun { 
    println("building the function") 
    val uuidOfThisFunction = UUID.randomUUID 
    x => println(s"$uuidOfThisFunction, $x") 
} 

Так что этот блок готовит функцию, придавая ему некоторый дополнительный контекст через закрытие. Этот uuid останется неизменным для обоих вызовов, которые произойдут, если fun.

building the function 
86e74b5e-83b5-41f3-a71c-eeafcd1db2a0, 10 
86e74b5e-83b5-41f3-a71c-eeafcd1db2a0, 20 

Пример, который будет выглядеть как то, что вы сделали с первым звонком будет

fun(
    x => { 
    println("calling the function") 
    val uuidOfThisCall = UUID.randomUUID 
    println(s"$uuidOfThisCall, $x") 
    } 
) 

Блок оценивает каждый раз, когда f называется.

calling the function 
d3c9ff0a-84b4-47a3-8153-c002fa16d6c2, 10 
calling the function 
a0b1aa5b-c0ea-4047-858b-9db3d43d4983, 20 
4

println(_) расширяется до x => println(x), так {println("hi"); println(_)} расширяется до {println("hi"); x => println(x)} поэтому, когда fun{println("hi"); println(_)} исполняет следующие шаги имеют место:?..

  1. выражение {{println("hi"); println(_)}} вычисляется это означает, :

    1. println("hi") оценивается, а затем
    2. x => println(x) оценивается, создавая функциональный объект, который будет печатать свой аргумент.
    3. Созданный таким образом объект функции является результатом выражения.
  2. Метод func вызывается с созданным функциональным объектом в качестве его аргумента. func вызовет функцию с 10 и 20, заставив ее распечатать эти цифры.
+0

Я думаю, что вы просто дали описание результата, а не причину. Как определение 'def fun (f: Int => Unit)', что такое 'f' в определении' fun {println ("hi"); println (_)} '? если 'println (" hi ")' не включен в 'f', почему? – Jerry

+1

Да, это то, что я действительно хочу знать. Фактически, я хочу реализовать эффект вызова метода 1, между тем я хочу просто вызвать «fun (i => {println (« hi »); println (i)})». Поэтому я использовал вызов метода 2, но найти эффект сильно отличается от вызова метода 1. Поэтому я хочу получить реальную причину, почему println («привет») не входит в f в вызове метода 2? – lagom

+0

@atline Я считаю, что я дал настоящую причину. '{Println ("привет"); println (_)} 'не расширяется до' {x => {println ("hi"); println (x)}} ', а скорее' {println ("hi"); x => println (x)} '. Вот почему вы видите поведение, которое видите. Причина, по которой он расширяется, заключается в том, что правила расширения довольно просты. Для этого случая соответствующее правило просто состоит в том, что 'f (args ..., _, ... moreArgs)' расширяется до 'x => f (args ..., _, ... moreArgs)'. Если вы хотите знать, почему правила такие, как они есть, вы можете спросить дизайн, но это, вероятно, сводится к тому, что простые правила, как правило, лучше, нужно смотреть на ... – sepp2k

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