Я читал руководство-неофиты по лестнице-10, где наткнулся на следующий код.
type EmailFilter = Email => Boolean
val minimumSize: Int => EmailFilter = n => email => email.text.size >= n
Я понял первую строку, в которой псевдоним типа EmailFilter создается для функции, которая принимает логическое значение возврата электронной почты. Но я не понимаю вторую строку, где мы берем адрес электронной почты и номер в качестве ввода и возвращаем логическое значение, проверяя размер. Пожалуйста, расшифруйте вторую строку и объясните мне этот синтаксический сахарный код для функции.





Нет никакого синтаксического сахара, только сырые лямбда-выражения.
Если вы вставите определение type EmailFilter в тип во второй строке, вы получите
Int => (Email => Boolean)
что то же самое (из-за правоассоциативности =>) как
Int => Email => Boolean
и это прекрасно соответствует
n => email => (email.text.size >= n)
который по сути просто говорит: учитывая число n, создайте фильтр электронной почты, который для данного email возвращает true тогда и только тогда, когда длина сообщения электронной почты не менее n., так что, например,
minimumSize(100)
возвращает закрытие, которое ведет себя так же, как
email => email.text.size >= 100
то есть он фильтрует все электронные письма, длина которых больше или равна 100. Таким образом, с подходящими примерами писем shortMail и longMail, вы получите:
minimumSize(100)(shortMail) // false
minimumSize(100)(longMail) // true
Функция minimumSize - это функция карри.
Каррирование - это способ разделить вызов функции на несколько последовательных вызовов подфункций.
У функции каррирования есть много хороших преимуществ, одно из которых состоит в том, что она позволяет вашей функции быть более компонуемой, откладывая реальный источник данных.
Изобразим использование:
n => email => email.text.size >= n
Сначала мы можем вызвать эту функцию, передав параметр только для n:
minimumSize(2) // partially applies the minimumSize function with 2 as n
Вы получите в это время:
val nextFunction = email => email.text.size >= 2
Затем вы звоните nextFunction по электронной почте:
nextFunction(Email("[email protected]"))
В этот раз вы получите логическое значение:
val bool = Email("[email protected]").text.size >= 2
Итак, если подвести итог:
Мы начали с Int, затем с Email, затем с Boolean:
Int => Email => Boolean
И, внимательно посмотрев на эту подпись, вы узнаете подпись EmailFilter.
Подставим:
Int => EmailFilter
Идея состоит в том, чтобы заставить EmailFilter действовать как шаблон, который вы можете параметризовать с помощью некоторых более высоких функций.
Здесь мы параметризовали сравнение размера текста электронной почты, чтобы мы могли оставить EmailFilter общим.
Имейте в виду, что функциональное программирование - это создание функций.
Но String по-прежнему не имеет члена .text, и тип Email не то же самое, что тип String для всех значимых определений типа Email. Не могли бы вы представить case class Email(text: String), чтобы сделать весь пример компилируемым?
Это концептуальная подстановка (как псевдокод), цель не в том, чтобы сделать компилятор счастливым, а в простом объяснении идеи.
Хорошо, тогда, может быть, ради концептуальной простоты можно было бы просто удалить .text из email.text.size и набрать type Email = String.
ха-ха, я как раз собирался заменить String на формулировку Email (я забыл об одном случае), но вы были быстрее :)
Mik378 Ага, спасибо, теперь яснее. Если бы я действительно хотел придираться, я бы сказал, что "[email protected]" больше похож на адрес, чем на текст, но ладно, теперь его, очевидно, можно расширить до компилируемого кода, (+1);)
Да спасибо :) Адрес потому что речь идет о рассылке, но согласился, что text не особо подходит.
На самом деле это не каррированная функция, это просто функция высшего порядка (HOF), то есть функция, которая возвращает функцию. Предполагается, что EmailFilter будет вызываться несколько раз, по одному на каждое электронное письмо. При каррировании вы преобразуете одну функцию с несколькими аргументами в несколько функций с одним аргументом. Когда вы вызываете каррированную функцию, вы вызываете все функции по очереди, вы не используете повторно никакие промежуточные функции.
minimumSize по определению является каррированным, даже если предполагается использовать его как функцию высшего порядка.
@ Mik378 Мне было бы интересно увидеть это определение. Если это каррированная функция, то незамещенной функцией будет minimumSize(Int, Email): Boolean, которая имеет неудачное название, потому что это просто операция сравнения. Я не верю, что функция предназначалась для вызова как minimumSize(10)(email), а скорее как emails.filter(minimumSize(10)), и поэтому я не верю, что это функция каррирования. YMMV
@ Mik378 Я не запутался, я просто не хочу, чтобы люди думали, что любая функция с одним аргументом, возвращающая функцию с одним аргументом, по определению является каррированной версией функции с двумя аргументами. Описание функции как curried подразумевает намерение, и я не вижу никаких доказательств этого намерения. YMMV
@ Тим Это карри. Взгляните на это видео Рунара Бьярнасона: youtu.be/ZasXwtTRkio ровно в 3:50 и послушайте, что он говорит в этот момент. (между 3:50 и 3:55). Кстати, то, что делает ваш emails.filter (minimumSize (10)), - это curring, поскольку вызов minimumSize (10) (currentEmail) будет происходить внутри.
@ Mik378 Я посмотрел. То, что докладчик называет каррированием, на самом деле является частичным применением, поэтому я не уверен, как это помогает.
Цель каррирования - отложить выполнение функции. Это похоже на частичное применение функции, возвращающей 1-унарную вложенную функцию. Каррирование всегда создает вложенные унарные (одномерные) функции. Преобразованная функция остается в основном такой же, как и оригинал. Частичное приложение производит функции произвольной степени. Преобразованная функция отличается от исходной - ей нужно меньше аргументов.
Что такое
'[email protected]'? Строковый литерал Python? Кроме того, уStringнет члена.text.