Объяснение частичной функции в книге Одерского - PullRequest
0 голосов
/ 17 мая 2018

В книге Скалы Одерского у него есть пример, объясняющий частичные функции страницы 295. Он начинается с этой функции:

val second: List[Int] => Int = {
    case x :: y :: _ => y
}

Таким образом, вышеуказанная функция будет успешной, если вы передадите ей список из трех элементов, но не пустой список.

second(List(5,6,7))

работает, но не

second(List())

Выше будет выбрасывать MatchError: List

Вот часть, которая смущает меня. Одерский пишет:

Если вы хотите проверить, определена ли частичная функция, вы должны сначала сообщить компилятору, что вы знаете, что работаете с частичными функциями.

Зачем мне проверять, определена ли частичная функция. Что такое частичная функция? Это функция, которая применяется только к некоторым значениям?

Тип List [Int] => Int включает все функции из списков целых чисел в целые, независимо от того, являются ли они частичными. Тип, который включает только частичные функции из списков целых чисел в целые, записывается как PartialFunction [List [Int], Int].

Таким образом, вышеприведенная функция возвращает функцию типа List [Int] => Int, я вижу это, но почему нам нужно изменить эту функцию на тип PartialFunction[List[Int], Int]?

Вот переопределенная функция:

val second: PartialFunction[List [Int], Int] = {
    case x :: y :: _ => y
}

Я действительно не понимаю. В чем выгода? Почему мы хотим проверить, определена ли частичная функция? Что это вообще значит?

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

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

val divide = (x: Int) => 42 / x

Как определено, эта функция взрывается, когда входной параметр равен нулю:

scala> divide(0)
java.lang.ArithmeticException: / by zero

Хотя выможет справиться с этой конкретной ситуацией, перехватывая и выбрасывая исключение, Scala позволяет определить функцию деления как PartialFunction.При этом вы также явно заявляете, что функция определяется, когда входной параметр не равен нулю:

val divide = new PartialFunction[Int, Int] {
def apply(x: Int) = 42 / x
def isDefinedAt(x: Int) = x != 0
}

https://alvinalexander.com/scala/how-to-define-use-partial-functions-in-scala-syntax-examples

Вы можете обратиться кссылка выше.

0 голосов
/ 17 мая 2018

A частичная функция - это любая функция, которая принимает только один аргумент, то есть определяется (то есть действует) только для определенного диапазона значений ее аргумента.Например, Math.asin определено только для значений аргументов в диапазоне [-1.0, 1.0] и не определено для значений за пределами этого диапазона - поэтому это частичная функция.Например, если мы вызываем Math.asin(5.0), мы получаем NaN, что означает, что функция не определена для этого аргумента.

Обратите внимание, что частичная функция не обязательно должна вызывать исключение;ему просто нужно сделать что-то иное, чем вернуть действительное значение.

Ключевой принцип функционального программирования - ссылочная прозрачность ( RT ),Это означает, что мы должны иметь возможность заменить выражение (например, вызов функции) значением этого выражения, не меняя смысла программы.(Для получения дополнительной информации по этой теме я настоятельно рекомендую вам прочитать Функциональное программирование в Scala от Chiusano и Bjarnason.) Очевидно, что это не работает, если выброшено исключение или недопустимое значениевозвращаетсяЧтобы обращения к частичным функциям были прозрачными по ссылкам, мы можем вызывать их только со значениями аргументов, для которых они определены, или нам нужно элегантно обрабатывать неопределенные значения.Итак, как мы можем определить, определена ли частичная функция для некоторого произвольного значения аргумента?

В Scala мы можем выразить частичные функции как подкласс scala.PartialFunction, которыйпозволяет нам ответить на этот вопрос.

Давайте рассмотрим ваш пример в Scala REPL сессии ...

$ scala
Welcome to Scala 2.12.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.

scala> val second: List[Int] => Int = {
     |     case x :: y :: _ => y
     | }
<console>:11: warning: match may not be exhaustive.
It would fail on the following inputs: List(_), Nil
       val second: List[Int] => Int = {
                                  ^
second: List[Int] => Int = $$Lambda$3181/1894473818@27492c62

Итак, что мы только что сделали?Мы определили second как ссылку на функцию, которая принимает аргумент List[Int] и возвращает Int (второе значение в списке).

Вы заметите, что компилятор Scala признает, что это не подходит для всех случаев, и предупреждает вас об этом.Это частичная функция, в том смысле, что она не работает для некоторых аргументов, но это не экземпляр scala.PartialFunction, так как мы можем проверить следующее:

scala> second.isInstanceOf[PartialFunction[List[Int], Int]]
res0: Boolean = false

Кстати, тип List[Int] => Intявляется сокращением для scala.Function1[List[Int], Int], поэтому тип second s является экземпляром этого типа:

scala> second.isInstanceOf[Function1[List[Int], Int]]
res1: Boolean = true

Вызов этой версии функции приводит к указанным вами результатам:

scala> second(List(1, 2, 3))
res1: Int = 2

scala> second(Nil)
scala.MatchError: List() (of class scala.collection.immutable.Nil$)
  at .$anonfun$second$1(<console>:11)
  at .$anonfun$second$1$adapted(<console>:11)
  ... 36 elided

Проблема в том, что если у нас просто есть какое-то значение списка l, и мы не знаем, что находится в этом списке, мы не знаем, получим ли мы исключение, если передадим его функции, на которую ссылается second.Теперь мы можем поместить вызов в try блок и catch в любом исключении, но это многословно и не очень хорошо функциональный стиль программирования .В идеале мы хотели бы знать, можем ли мы сначала вызвать функцию, чтобы избежать исключения.К сожалению, из экземпляра Function1 невозможно определить:

scala> second.isDefinedAt(Nil)
<console>:13: error: value isDefinedAt is not a member of List[Int] => Int
       second.isDefinedAt(Nil)
              ^

Нам нужно объявить second, чтобы иметь тип PartialFunction[List[Int], Int] следующим образом:

scala> val second: PartialFunction[List[Int], Int] = {
     |   case x :: y :: _ => y
     | }
second: PartialFunction[List[Int],Int] = <function1>

(Кстати, обратите внимание, что у вас есть опечатка в вашем вопросе для этого кода - выше, как это должно быть определено.)

Теперь у нас нет никаких предупреждений!Мы сказали компилятору, что это экземпляр PartialFunction, поэтому компилятор знает, что он не определен для некоторых аргументов, поэтому предупреждения излишни.Теперь мы можем проверить этот факт:

scala> second.isInstanceOf[PartialFunction[List[Int], Int]]
res6: Boolean = true

Теперь мы можем также проверить, определен ли он для определенных значений:

scala> second.isDefinedAt(Nil)
res7: Boolean = false

scala> second.isDefinedAt(List(1, 2))
res9: Boolean = true

и так далее.(Компилятор Scala , как описано в книге, может реализовать для нас эту волшебную функцию isDefinedAt.)

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

def getSecondValue(l: List[Int]): Option[Int] = {

  // Check if second is defined for this argument. If so, call it and wrap in Some.
  if(second.isDefinedAt(l)) Some(second(l))

  // Otherwise, we do not have a second value.
  else None
}

Ну, это тоже немного многословно.К счастью, когда second является экземпляром PartialFunction, мы можем переписать приведенное выше как:

def getSecondValue(l: List[Int]): Option[Int] = second.lift(l)

Метод lift превращает частичную функцию в завершенную функцию , которая возвращаетопределенное значение для каждого аргумента: если аргумент для second определен, то мы получим Some(value);в противном случае мы получим None.

Вы найдете понятие частичных функций, и PartialFunction, более полезным, когда вы познакомитесь с функциональным программированием .Если вы не получите это прямо сейчас, не волнуйтесь;все станет ясно.

...