Использование частичных функций в Scala-как это работает?



Я новичок в Scala, я использую 2.9.1, и я пытаюсь понять, как использовать частичные функции. У меня есть базовое понимание функций Карри, и я знаю, что частичные функции подобны функциям Карри, где они только 2nary или что-то подобное. Как вы можете заметить, я немного позеленел в этом.



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



У меня есть функция, которая использует структуру RewriteRule, но мне нужно, чтобы она работала с двумя аргументами, в то время как структура RewriteRule принимает только одну или частичную функцию. Я думаю, что это один из тех случаев, когда я думаю, что это полезно.



Любые советы, ссылки, мудрые слова и т.д. добро пожаловать!



Ответы на данный момент превосходны и прояснили несколько фундаментальных заблуждений, которые у меня есть. Я думаю, что они также объясняют, где я борюсь - я думаю, что, возможно, размещение новый вопрос, который будет немного более конкретным, поможет, поэтому я сделаю это тоже.

546   2  

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.7320508075688772
Это полезно, когда у вас есть что-то, что знает, как проверить, определена ли функция или нет. Соберите, например:
scala> 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

    Ничего не найдено.