Scala foreach странное поведение

Есть возможность использования отражения ...

var param = "Address";    
var propertyInfo = typeof(Student).GetProperty(param);    
var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null));
41
задан F0RR 31 January 2010 в 21:15
поделиться

5 ответов

Это:

x foreach println(_ + 1)

эквивалентно этому:

x.foreach(println(x$1 => x$1 + 1))

Нет никаких указаний на то, что может быть типом x $ 1 , и, честно говоря, нет никакого смысла печатать функцию.

Очевидно (для меня) вы хотели напечатать x $ 0 + 1 , где x $ 0 вместо этого будет параметром, переданным foreach . Но давайте рассмотрим это ... foreach принимает в качестве параметра Function1 [T, Unit] ,где T - параметр типа списка. Вместо этого в foreach вы передаете println (_ + 1) , то есть выражение, возвращающее Unit .

Если бы вы написали вместо x foreach println , вы бы передали совершенно другое. Вы должны передать функцию (*) println , которая принимает Any и возвращает Unit , что соответствует требованиям foreach ].

Это немного сбивает с толку из-за правил расширения _ . Он расширяется до самого внутреннего разделителя выражений (скобок или фигурных скобок), за исключением случаев, когда они используются вместо параметра, и в этом случае это означает другое: частичное применение функции.

Чтобы лучше объяснить это, рассмотрите следующие примеры:

def f(a: Int, b: Int, c: Int) = a + b + c
val g: Int => Int = f(_, 2, 3) // Partial function application
g(1)

Здесь мы применяем второй и третий аргументы к f и возвращаем функцию, требующую только оставшийся аргумент. Обратите внимание, что он работал только как есть, потому что я указал тип g , иначе мне пришлось бы указать тип аргумента, который я не применял. Продолжим:

val h: Int => Int = _ + 1 // Anonymous function, expands to (x$1: Int => x$1 + 1)
val i: Int => Int = (_ + 1) // Same thing, because the parenthesis are dropped here
val j: Int => Int = 1 + (_ + 1) // doesn't work, because it expands to 1 + (x$1 => x$1 + 1), so it misses the type of `x$1`
val k: Int => Int = 1 + ((_: Int) + 1) // doesn't work, because it expands to 1 + (x$1: Int => x$1 + 1), so you are adding a function to an `Int`, but this operation doesn't exist

Давайте обсудим k более подробно, потому что это очень важный момент. Вспомните, что g - это функция Int => Int , верно? Итак, если бы я набрал 1 + g , был бы в этом какой-то смысл? Это то, что было сделано в k .

Людей смущает то, что они действительно хотели:

val j: Int => Int = x$1 => 1 + (x$1 + 1)

Другими словами, они хотят x $ 1 вместо _ , чтобы перейти на за пределами скобку и в нужное место. Проблема здесь в том, что, хотя им может показаться очевидным правильное место, компилятору это не очевидно. Рассмотрим этот пример, например:

def findKeywords(keywords: List[String], sentence: List[String]) = sentence.filter(keywords contains _.map(_.toLowerCase))

Теперь, если бы мы расширили это за пределы скобок, мы получили бы следующее:

def findKeywords(keywords: List[String], sentence: List[String]) = (x$1, x$2) => sentence.filter(keywords contains x$1.map(x$2.toLowerCase))

Что определенно не то, что мы хотим. Фактически, если бы _ не было ограничено самым внутренним разделителем выражения, нельзя было бы использовать _ с вложенной картой , flatMap , фильтр и foreach .

Теперь вернемся к путанице между анонимной функцией и частичным приложением. Посмотрите здесь:

List(1,2,3,4) foreach println(_) // doesn't work
List(1,2,3,4) foreach (println(_)) // works
List(1,2,3,4) foreach (println(_ + 1)) // doesn't work

Первая строка не работает из-за того, как работает нотация операций. Scala просто видит, что println возвращает Unit , чего не ожидает foreach .

Вторая строка работает, потому что скобки позволяют Scala оценивать println (_) в целом. Это приложение с частичной функцией, поэтому оно возвращает Any => Unit , что допустимо.

Третья строка не работает, потому что _ + 1 - анонимная функция, которую вы передаете в качестве параметра println . Вы не делаете println частью анонимной функции, чего вы хотели.

Наконец, чего ожидают немногие:

List(1,2,3,4) foreach (Console println _ + 1)

Это работает.Почему это происходит, оставлено читателю в качестве упражнения. : -)

(*) На самом деле println - это метод. Когда вы пишете x foreach println , вы не передаете метод, потому что методы не могут быть переданы. Вместо этого Scala создает замыкание и передает его. Он расширяется следующим образом:

x.foreach(new Function1[Any,Unit] { def apply(x$1: Any): Unit = Console.println(x$1) })
69
ответ дан 27 November 2019 в 00:32
поделиться
scala> for(x <- List(1,2,3,4)) println(x + 1)
2
3
4
5
5
ответ дан Viktor Klang 12 November 2019 в 11:15
поделиться
Welcome to Scala version 2.8.0.Beta1-prerelease (Java HotSpot(TM) Client VM, Java 1.6.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val l1 = List(1, 2, 3)
l1: List[Int] = List(1, 2, 3)

scala>

scala> l1.foreach(println(_))
1
2
3
-3
ответ дан Randall Schulz 12 November 2019 в 11:15
поделиться

Подчеркивание немного сложно. Согласно спецификации, фраза:

_ + 1

эквивалентна

x => x + 1

, пытаясь

x foreach println (y => y + 1)

выходу:

<console>:6: error: missing parameter type
           x foreach println (y => y + 1)

Если вы добавляете некоторые типы в:

x foreach( println((y:Int) => y + 1))
<console>:6: error: type mismatch;
 found   : Unit
 required: (Int) => Unit
           x foreach( println((y:Int) => y + 1))

проблема в том, что вы передаете анонимную функцию PrintLn , и он не может с этим иметь дело. Что вы действительно хотите сделать (если вы пытаетесь распечатать преемника каждому элементу в списке):

x map (_+1) foreach println
13
ответ дан 27 November 2019 в 00:32
поделиться

В Scala существует странное ограничение на глубину вложенности выражений с подчеркиванием. Оно хорошо видно на следующем примере:

 scala> List(1) map(1+_)
 res3: List[Int] = List(2)

 scala> Some(1) map (1+(1+_))
 <console>:5: error: missing parameter type for expanded function ((x$1) => 1.+(x$1))
        Some(1) map (1+(1+_))
                     ^

Похоже на ошибку.

-3
ответ дан 27 November 2019 в 00:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: