Использование частичных функций в Scala-как это работает?
Я новичок в Scala, я использую 2.9.1, и я пытаюсь понять, как использовать частичные функции. У меня есть базовое понимание функций Карри, и я знаю, что частичные функции подобны функциям Карри, где они только 2nary или что-то подобное. Как вы можете заметить, я немного позеленел в этом.
Похоже, что в некоторых случаях, таких как фильтрация XML, возможность частичных функций была бы очень выгодна, поэтому я надеюсь получить лучшее понимание того, как использовать их.
У меня есть функция, которая использует структуру RewriteRule, но мне нужно, чтобы она работала с двумя аргументами, в то время как структура RewriteRule принимает только одну или частичную функцию. Я думаю, что это один из тех случаев, когда я думаю, что это полезно.
Любые советы, ссылки, мудрые слова и т.д. добро пожаловать!
Ответы на данный момент превосходны и прояснили несколько фундаментальных заблуждений, которые у меня есть. Я думаю, что они также объясняют, где я борюсь - я думаю, что, возможно, размещение новый вопрос, который будет немного более конкретным, поможет, поэтому я сделаю это тоже.
2 ответов:
Частичная функция-это функция, допустимая только для подмножества значений тех типов, которые вы могли бы ей передать. Например:
Это полезно, когда у вас есть что-то, что знает, как проверить, определена ли функция или нет. Соберите, например:val root: PartialFunction[Double,Double] = { case d if (d >= 0) => math.sqrt(d) } scala> root.isDefinedAt(-1) res0: Boolean = false scala> root(3) res1: Double = 1.7320508075688772scala> List(0.5, -0.2, 4).collect(root) // List of _only roots which are defined_ res2: List[Double] = List(0.7071067811865476, 2.0)Этоне поможет вам разместить два аргумента там, где вы действительно хотите один.
Напротив, частично применяемая функция - это функция, в которой часть ее аргументы уже были заполнены.
def add(i: Int, j: Int) = i + j val add5 = add(_: Int,5)Теперь вам нужен только один аргумент-вещь, чтобы добавить 5 к-вместо двух:
scala> add5(2) res3: Int = 7Из этого примера вы можете увидеть, как его использовать.
Но если вам нужно указать эти два аргумента, этот все равно этого не сделает-скажем, вы хотите использовать
map, например, и вам нужно дать ему функцию одного аргумента, но вы хотите, чтобы он добавил две разные вещи. Ну, тогда вы можетеval addTupled = (add _).tupledКоторый будет частично применяться функция (на самом деле, просто создайте функцию из метода, так как ничего не было заполнено), а затем объедините отдельные аргументы в кортеж. Теперь вы можете использовать это в тех местах, где требуется один аргумент (при условии, что тип правильный):
scala> List((1,2), (4,5), (3,8)).map(addTupled) res4: List[Int] = List(3, 9, 11)Напротив, карринг еще раз отличается; он превращает функции вида
(A,B) => CвA => B => C. То есть, если задана функция с несколькими аргументами, она будет производить цепочку функций, каждая из которых принимает один аргумент и верните цепочку на единицу короче (вы можете думать о ней как о частичном применении одного аргумента за раз).val addCurried = (add _).curried scala> List(1,4,3).map(addCurried) res5: List[Int => Int] = List(<function1>, <function1>, <function1>) scala> res5.head(2) // is the first function, should add 1 res6: Int = 3 scala> res5.tail.head(5) // Second function should add 4 res7: Int = 9 scala> res5.last(8) // Third function should add 3 res8: Int = 11
Объяснение Рекса Керра очень хорошее-и в этом тоже нет ничего удивительного. Ясно, что речь идет о смешении частичных функций с частично прикладными функциями. Как бы то ни было, я сам сделал такую же путаницу, когда изучал скалу.
Однако, поскольку этот вопрос действительно привлекает внимание к частичным функциям, я хотел бы немного поговорить о них.Многие люди говорят, что частичные функции-это функции, которые не определены для всех входных данных, и это это верно для математики, но не для Скалы. В Scala функция также не может быть определена для всех входных данных. Фактически, поскольку частичная функция наследуется от функции, то функция включает в себя и все частичные функции, что делает это неизбежным.
Другие упоминают метод
isDefinedAt, который, действительно, отличается, но в основном касается реализации. Это настолько верно, что Scala 2.10, вероятно, будет выпущен с "быстрой частичной функцией", которая не зависит отisDefinedAt.И несколько человек даже подразумевают, что метод
applyдля частичных функций делает что-то другое, чем методapplyдля функций, например, только выполнение для входных данных, которые определены-что не может быть дальше от истины. Методapplyявляется точно таким же.К чему на самом деле сводятся частные функции, так это к другому методу:
orElse. Это суммирует все случаи использования для частичных функций намного лучше, чемisDefinedAt, потому что частичные функции на самом деле выполняют одно из следующих действий вещи:Я не говорю, что все может быть легко реализовано в терминах
- цепочка частичных функций (что и делает
orElse), так что входные данные будут опробованы на каждой частичной функции, пока одна из них не совпадет.- делать что-то другое, если частичная функция не совпадает, вместо того, чтобы создавать исключение, что произойдет, если вы свяжете эту другую вещь с помощью
orElse.orElse, заметьте. Я просто говорю, что частично функции состоят в том, чтобы делать что-то еще, когда входные данные для него не определены.
Comments