代码之家  ›  专栏  ›  技术社区  ›  oxbow_lakes

函数等价于if(p(f(a),f(b))a else b

  •  14
  • oxbow_lakes  · 技术社区  · 15 年前

    我想必须有一种更好的功能性表达方式:

    def foo(i: Any) : Int
    
    if (foo(a) < foo(b)) a else b 
    

    所以在这个例子中 f == foo p == _ < _ . 在斯卡拉斯一定会有一些绝妙的聪明!我可以看到使用 BooleanW 我可以写:

    p(f(a), f(b)).option(a).getOrElse(b)
    

    但我确信我可以写一些代码 仅指 一旦 . 如果存在这种情况,它必须是 Function1W 除斯卡拉斯以外的其他东西对我来说有点神秘!

    编辑 : 我想我这里要问的不是“我怎么写这个?”但是“对于这样一个函数,正确的名称和签名是什么?它与我还不理解的fp有关吗,比如kleisli、comonad等等?”

    6 回复  |  直到 13 年前
        1
  •  6
  •   Thomas Jung    15 年前

    以防它不在斯卡拉斯:

    def x[T,R](f : T => R)(p : (R,R) => Boolean)(x : T*) =
      x reduceLeft ((l, r) => if(p(f(l),f(r))) r else l)
    
    scala> x(Math.pow(_ : Int,2))(_ < _)(-2, 0, 1)
    res0: Int = -2
    

    另一种方法是使用一些开销较大但语法更好的方法。

    class MappedExpression[T,R](i : (T,T), m : (R,R)) {
      def select(p : (R,R) => Boolean ) = if(p(m._1, m._2)) i._1 else i._2 
    }
    
    class Expression[T](i : (T,T)){
      def map[R](f: T => R) = new MappedExpression(i, (f(i._1), f(i._2)))
    }
    
    implicit def tupleTo[T](i : (T,T)) = new Expression(i)
    
    scala> ("a", "bc") map (_.length) select (_ < _)
    res0: java.lang.String = a
    
        2
  •  5
  •   Tomas Petricek    15 年前

    我不认为箭头或任何其他特殊类型的计算在这里是有用的。毕竟,你是用正常值计算的,你通常可以 纯净的 进入特殊计算类型的计算(使用 arr 箭或 return 对于单子)。

    然而,一个非常简单的箭头是 arr a b 只是一个函数 a -> b . 然后可以使用箭头将代码拆分为更多的原始操作。但是,可能没有理由这样做,它只会使代码更加复杂。

    例如,你可以把电话转到 foo 因此,这是与比较分开进行的。下面是f中箭头的一个类似定义——它声明 *** >>> 箭头组合键和 ARR 将纯函数转换为箭头:

    type Arr<'a, 'b> = Arr of ('a -> 'b)
    let arr f = Arr f
    let ( *** ) (Arr fa) (Arr fb) = Arr (fun (a, b) -> (fa a, fb b))
    let ( >>> ) (Arr fa) (Arr fb) = Arr (fa >> fb)
    

    现在您可以这样编写代码:

    let calcFoo = arr <| fun a -> (a, foo a)    
    let compareVals = arr <| fun ((a, fa), (b, fb)) -> if fa < fb then a else b
    
    (calcFoo *** calcFoo) >>> compareVals
    

    这个 *** Combinator接受两个输入,分别在第一个和第二个参数上运行第一个和第二个指定函数。 >>> 然后用做比较的箭头组成这个箭头。

    但正如我所说的,写这篇文章可能根本没有理由。

        3
  •  4
  •   retronym    15 年前

    这里是基于箭头的解决方案,用scalaz实现。这需要后备箱。

    将箭头抽象与简单的旧函数结合使用并不会获得巨大的成功,但这是在转到Kleisli或Cokleisli箭头之前学习它们的一种好方法。

    import scalaz._
    import Scalaz._
    
    def mod(n: Int)(x: Int) = x % n
    def mod10 = mod(10) _
    def first[A, B](pair: (A, B)): A = pair._1
    def selectBy[A](p: (A, A))(f: (A, A) => Boolean): A = if (f.tupled(p)) p._1 else p._2
    def selectByFirst[A, B](f: (A, A) => Boolean)(p: ((A, B), (A, B))): (A, B) =
      selectBy(p)(f comap first) // comap adapts the input to f with function first.
    
    val pair = (7, 16)
    
    // Using the Function1 arrow to apply two functions to a single value, resulting in a Tuple2
    ((mod10 &&& identity) apply 16) assert_≟ (6, 16)
    
    // Using the Function1 arrow to perform mod10 and identity respectively on the first and second element of a `Tuple2`.
    val pairs = ((mod10 &&& identity) product) apply pair
    pairs assert_≟ ((7, 7), (6, 16))
    
    // Select the tuple with the smaller value in the first element.
    selectByFirst[Int, Int](_ < _)(pairs)._2 assert_≟ 16
    
    // Using the Function1 Arrow Category to compose the calculation of mod10 with the
    // selection of desired element.
    val calc = ((mod10 &&& identity) product) ⋙ selectByFirst[Int, Int](_ < _)
    calc(pair)._2 assert_≟ 16
    
        4
  •  3
  •   Community CDub    7 年前

    嗯,我抬头看了看 Hoogle 对于像中的类型签名 Thomas Jung's answer 还有 on . 这是我搜索的内容:

    (a -> b) -> (b -> b -> Bool) -> a -> a -> a
    

    在哪里? (a -> b) 相当于 foo , (b -> b -> Bool) 相当于 < . 不幸的是,签名 返回其他内容:

    (b -> b -> c) -> (a -> b) -> a -> a -> c
    

    如果你更换的话,这几乎是一样的 c 具有 Bool a 在这两个地方,它分别出现。

    所以,现在,我怀疑它不存在。我发现有一个更通用的类型签名,所以我也尝试了它:

    (a -> b) -> ([b] -> b) -> [a] -> a
    

    这一次一无所获。

    编辑:

    现在我觉得我一点也不远了。例如,考虑:

    Data.List.maximumBy (on compare length) ["abcd", "ab", "abc"]
    

    函数 maximumBy 签名是 (a -> a -> Ordering) -> [a] -> a ,与 ,与您最初指定的非常接近,因为 Ordering 有三个值——几乎是布尔值!-)

    所以,假设你写了 在斯卡拉:

    def on[A, B, C](f: ((B, B) => C), g: A => B): (A, A) => C = (a: A, b: A) => f(g(a), g(b))
    

    你可以写的 select 这样地:

    def select[A](p: (A, A) => Boolean)(a: A, b: A) = if (p(a, b)) a else b
    

    像这样使用:

    select(on((_: Int) < (_: Int), (_: String).length))("a", "ab")
    

    这真的更好地与货币和无点符号。:-)但是让我们尝试一下隐式:

    implicit def toFor[A, B](g: A => B) = new { 
      def For[C](f: (B, B) => C) = (a1: A, a2: A) => f(g(a1), g(a2)) 
    }
    implicit def toSelect[A](t: (A, A)) = new { 
      def select(p: (A, A) => Boolean) = t match { 
        case (a, b) => if (p(a, b)) a else b 
      } 
    }
    

    然后你可以写

    ("a", "ab") select (((_: String).length) For (_ < _))
    

    非常接近。我还没有找到从中删除类型限定符的任何方法,尽管我怀疑这是可能的。我是说,不走托马斯的路。但也许 路。事实上,我想 on (_.length) select (_ < _) 读得比 map (_.length) select (_ < _) .

        5
  •  1
  •   missingfaktor Kevin Wright    13 年前

    这个表达法可以写得非常优美 Factor programming language -函数组成是 这个 做事情的方式,大多数代码都是以无点的方式编写的。堆栈语义和行多态性促进了这种编程风格。这就是问题的解决方案在因素中的样子:

    # We find the longer of two lists here. The expression returns { 4 5 6 7 8 }
    { 1 2 3 } { 4 5 6 7 8 } [ [ length ] bi@ > ] 2keep ?
    
    # We find the shroter of two lists here. The expression returns { 1 2 3 }.
    { 1 2 3 } { 4 5 6 7 8 } [ [ length ] bi@ < ] 2keep ?
    

    我们感兴趣的是组合器 2keep . 它是一个“保留数据流组合器”,即在给定的函数对其执行之后保留其输入。


    让我们试着把这个解决方案翻译成scala。

    首先,我们定义了一个保留arity-2的组合器。

    scala> def keep2[A, B, C](f: (A, B) => C)(a: A, b: B) = (f(a, b), a, b)
    keep2: [A, B, C](f: (A, B) => C)(a: A, b: B)(C, A, B)
    

    和一个 eagerIf 组合器。 if 作为控制结构不能用于函数组合,因此该构造。

    scala> def eagerIf[A](cond: Boolean, x: A, y: A) = if(cond) x else y
    eagerIf: [A](cond: Boolean, x: A, y: A)A
    

    此外, on 组合器。因为它与scalaz中同名的方法冲突,所以我将命名它 upon 相反。

    scala> class RichFunction2[A, B, C](f: (A, B) => C) {
         |   def upon[D](g: D => A)(implicit eq: A =:= B) = (x: D, y: D) => f(g(x), g(y))
         | }
    defined class RichFunction2
    
    scala> implicit def enrichFunction2[A, B, C](f: (A, B) => C) = new RichFunction2(f)
    enrichFunction2: [A, B, C](f: (A, B) => C)RichFunction2[A,B,C]
    

    现在把这个机器投入使用!

    scala> def length: List[Int] => Int = _.length
    length: List[Int] => Int
    
    scala> def smaller: (Int, Int) => Boolean = _ < _
    smaller: (Int, Int) => Boolean
    
    scala> keep2(smaller upon length)(List(1, 2), List(3, 4, 5)) |> Function.tupled(eagerIf)
    res139: List[Int] = List(1, 2)
    
    scala> def greater: (Int, Int) => Boolean = _ > _
    greater: (Int, Int) => Boolean
    
    scala> keep2(greater upon length)(List(1, 2), List(3, 4, 5)) |> Function.tupled(eagerIf)
    res140: List[Int] = List(3, 4, 5)
    

    这种方法在scala中看起来并不特别优雅,但至少它向您展示了一种做事情的方法。

        6
  •  1
  •   Apocalisp    13 年前

    有一种很好的方法 on Monad 但不幸的是,scala在无点编程方面非常糟糕。你的问题基本上是:“我能减少这个项目的点数吗?”

    想象一下 if 不同的课程和培训:

    def on2[A,B,C](f: A => B)(g: (B, B) => C): ((A, A)) => C = {
      case (a, b) => f.on(g, a, b)
    }
    def if2[A](b: Boolean): ((A, A)) => A = {
      case (p, q) => if (b) p else q
    }
    

    然后你可以用读卡器Monad:

    on2(f)(_ < _) >>= if2
    

    哈斯克尔等效物为:

    on' (<) f >>= if'
      where on' f g = uncurry $ on f g
            if' x (y,z) = if x then y else z
    

    或者…

    flip =<< flip =<< (if' .) . on (<) f
      where if' x y z = if x then y else z