【问题标题】:How to convert one partial function to another?如何将一个部分函数转换为另一个?
【发布时间】:2018-02-05 08:00:14
【问题描述】:

假设我有部分函数parf

val parf: PartialFunction[Int, String] = { case 0 => "!!!" }

现在我也有了case class A(x: Int),我需要一个函数将PartialFunction[Int, String] 转换为PartialFunction[A, String]

def foo(pf: PartialFunction[Int, String]): PartialFunction[A, String] = ???

例如,foo(parf) 应该返回 {case A(0) => "!!!" }。您将如何编写函数 foo

【问题讨论】:

    标签: scala partialfunction


    【解决方案1】:

    为了保持正确的功能,您需要检查内部偏函数是否定义在您要传递的参数上:

    val parf: PartialFunction[Int, String] = { case 0 => "!!!" }
    
    case class A(x: Int)
    
    def foo(pf: PartialFunction[Int, String]): PartialFunction[A, String] = {
      case A(i) if pf.isDefinedAt(i) => pf(i)
    }
    

    如果您打算更大规模地执行此操作,您可能希望将部分函数转换为提取器对象,以便可以使用更好的语法直接在模式匹配中使用它:

    trait Extractor[A, B] {
      def unapply(a: A): Option[B]
    }
    
    object Extractor {
      implicit def partialFunctionAsExtractor[A, B](pf: PartialFunction[A, B]): Extractor[A, B] =
        new Extractor[A, B] {
          def unapply(a: A) = if (pf.isDefinedAt(a)) Some(pf(a)) else None
        }
    }
    
    def foo2(pf: Extractor[Int, String]): PartialFunction[A, String] = {
        case A(pf(str)) => str
    }
    
    foo2(parf) // implicit conversion magic
    

    【讨论】:

      【解决方案2】:

      我不明白是什么让你对此感到困惑?您只需要将IntA 中匹配提取出来,然后让PF 按照它想要的方式运行。

      scala> case class A(x: Int)
      // defined class A
      
      scala> val parf: PartialFunction[Int, String] = { case 0 => "!!!" }
      // parf: PartialFunction[Int,String] = <function1>
      
      scala> def foo(pf: PartialFunction[Int, String]): PartialFunction[A, String] = { 
           |   case A(x) if pf.isDefinedAt(x) => pf(x)
           | }   
      // foo: (pf: PartialFunction[Int,String])PartialFunction[A,String]
      
      scala> val parfA = foo(parf)
      // parfA: PartialFunction[A,String] = <function1>
      
      scala> parfA(A(0))
      //res0: String = !!!
      
      scala> parfA(A(1))
      // scala.MatchError: A(1) (of class A)
      //   at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:254)
      //   at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:252)
      //   at $anonfun$1.applyOrElse(<console>:11)
      //   at $anonfun$1.applyOrElse(<console>:11)
      //   at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:34)
      //   at $anonfun$foo$1.applyOrElse(<console>:13)
      //   at $anonfun$foo$1.applyOrElse(<console>:13)
      //   at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:34)
      //   ... 28 elided
      

      【讨论】:

      • 这将破坏 isDefinedAtlift 在新函数上的行为
      • @OlegPyzhcov 是的。好点。这就是为什么 - I don't see what got you confused about it?
      【解决方案3】:

      @Oleg Pyzhcov 已经提供了一个很好的解决方案。另一种方法是创建一个在 A(0) 处定义的 PartialFunction[A, Int],并使用andThen 将其与parf 链接起来:

      val parf: PartialFunction[Int, String] = { case 0 => "!!!" }
      
      case class A(n: Int)
      
      val bar: PartialFunction[A, Int] = { case a: A if a.n == 0 => a.n }
      
      def foo(pf: PartialFunction[Int, String]): PartialFunction[A, String] =
        bar andThen pf
      // foo: (pf: PartialFunction[Int,String])PartialFunction[A,String]
      
      foo(parf)
      // res1: PartialFunction[A,String] = <function1>
      

      【讨论】:

        猜你喜欢
        • 2022-06-17
        • 2023-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-10
        • 2017-08-12
        相关资源
        最近更新 更多