Scala, частичные функции

Есть ли способ создать PartialFunction, кроме как с помощью оператора case?

Мне любопытно, потому что я хотел бы выразить следующее (скала-псевдо вперед!)...

val bi = BigInt(_)
if (bi.isValidInt) bi.intValue

... как частичная функция, и выполнение

val toInt : PartialFunction[String, Int] = {
    case s if BigInt(s).isValidInt => BigInt(s).intValue
}

кажется излишним, так как я дважды создаю BigInt.


person aioobe    schedule 14.04.2011    source источник
comment
Я думаю, что важным вопросом, помимо набора текста, является: что мне нужно вернуть/выбросить в неопределенном случае, чтобы он был распознан как таковой orElse и др.?   -  person Raphael    schedule 15.04.2011


Ответы (5)


Не уверен, что понимаю вопрос. Но вот моя попытка: почему бы не создать экстрактор?

object ValidBigInt {
  def unapply(s: String): Option[Int] = {
    val bi = BigInt(s)
    if (bi.isValidInt) Some(bi.intValue) else None
  }
}

val toInt: PartialFunction[String, Int] = {
  case ValidBigInt(i) => i
}

Другой вариант (и это может ответить на вопрос, можно ли создать PartialFunction иначе, чем с литералом case):

val toInt = new PartialFunction[String, Int] {
  def isDefinedAt(s: String) = BigInt(s).isValidInt
  def apply(s: String) = BigInt(s).intValue
}

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

Я видел проект на Github, в котором пытались обойти это путем некоторого кэширования результатов из isDefinedAt. . Если вы спуститесь к бенчмаркам, то увидите, что он оказался медленнее, чем дефолтная реализация Scala :)

Поэтому, если вы хотите обойти двойную природу isDefinedAt по сравнению с apply, вам следует сразу перейти к (полной) функции, которая в результате дает Option[Int].

person 0__    schedule 14.04.2011
comment
Причина, по которой мне нужна частичная функция, заключается в том, что я добавляю ее после комбинатора парсера ^?. Мне нравится твоя идея с unapply. Никогда не использовал это. Спасибо! - person aioobe; 14.04.2011
comment
На самом деле, при правильных условиях кэширующие варианты частичной функции работают быстрее; ему нужно сделать некоторые оптимизации вручную, но я в шоке, что JIT этого не делает. - person Blaisorblade; 21.04.2012

Я думаю, вы ищете подъем/неподъем. lift берет частичную функцию и превращает ее в функцию, возвращающую Option. Unlift принимает функцию с одним аргументом, которая возвращает опцию, и возвращает частичную функцию.

import scala.util.control.Exception._

scala> def fn(s: String) = catching(classOf[NumberFormatException]) opt {BigInt(s)}
fn: (s: String)Option[scala.math.BigInt]

scala> val fnPf = Function.unlift(fn)
fnPf: PartialFunction[String,scala.math.BigInt] = <function1>

scala> val fn = fnPf.lift
fn: String => Option[scala.math.BigInt] = <function1>

Тесно связанный, вы также хотите посмотреть этот ответ для информации о cond и condOpt:

scala> import PartialFunction._
import PartialFunction._

scala> cond("abc") { case "def" => true }
res0: Boolean = false

scala> condOpt("abc") { case x if x.length == 3 => x + x }
res1: Option[java.lang.String] = Some(abcabc)
person James Moore    schedule 15.11.2011

Вы можете написать PartialFunction "от руки", если хотите:

object pf extends PartialFunction[Int,String] {
  def isDefinedAt(in: Int) = in % 2 == 0

  def apply(in: Int) = {
    if (in % 2 == 0) 
      "even" 
    else 
      throw new MatchError(in + " is odd")
}
person Alex Cruise    schedule 15.04.2011
comment
Итак, MatchError сигнализирует о неопределенности? - person Raphael; 15.04.2011
comment
Что ж, вы можете делать здесь все, что хотите, но генерация литерального кода PartialFunction в основном просто встраивает выражение сопоставления с образцом в метод применения, и это то, что делают выражения сопоставления с образцом в последнем случае (по крайней мере, концептуально — код, сгенерированный методом сопоставление с образцом очень интересно читать!) - person Alex Cruise; 16.04.2011

Хорошо, я получил это

import java.lang.NumberFormatException
import scala.util.control.Exception._

val toInt: PartialFunction[String, Int] = {
  catching(classOf[NumberFormatException]) opt BigInt(_) match {
    case Some(bi) if bi.isValidInt => bi.intValue
  }
}
person elbowich    schedule 06.03.2012

Как насчет этого?

val toInt: PartialFunction[String, Int] = (s: String) => BigInt(s) match {
  case bi if bi.isValidInt => bi.intValue
}
person elbowich    schedule 15.09.2011
comment
toInt.isDefinedAt("hej") выдает исключение вместо возврата false. :-/ - person aioobe; 16.09.2011
comment
Ну, это бросает NumberFormatException на BigInteger конструкцию, которая ожидается. - person elbowich; 16.09.2011
comment
Тогда единственное решение, которое я вижу, - это решение Sciss, но обертывание unapply в блоке try-catch, возвращающем None при исключении. - person elbowich; 16.09.2011