[英]Functional equivalent of if (p(f(a), f(b)) a else b
我猜测必须有更好的功能方式来表达以下内容:
def foo(i: Any) : Int
if (foo(a) < foo(b)) a else b
所以在这个例子中f == foo
和p == _ < _
。 对于scalaz来说,必然会有一些熟练的聪明才智! 我可以看到使用BooleanW
我可以写:
p(f(a), f(b)).option(a).getOrElse(b)
但我确信我能够编写一些只引用a和b一次的代码 。 如果存在,它必须是Function1W
和其他东西的某种组合,但scalaz对我来说有点神秘!
编辑 : 我想我在这里问的不是“我怎么写这个?” 但是“这个功能的正确名称和签名是什么?它与FP的东西有什么关系,我还不知道像Kleisli,Comonad等?”
万一它不在Scalaz中:
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
我不认为Arrows或任何其他特殊类型的计算在这里有用。 毕竟,你使用正常值进行计算,你通常可以将纯计算提升到特殊类型的计算中(使用arr
表示箭头或return
monad)。
然而,一个非常简单的箭头是arr ab
只是一个函数a -> b
。 然后,您可以使用箭头将代码拆分为更原始的操作。 但是,可能没有理由这样做,它只会使您的代码更复杂。
例如,您可以将调用解除为foo
以便与比较分开完成。 这是F#中箭的simiple定义-它声明***
和>>>
箭头组合程序,也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
***
组合器接受两个输入并在第一个和第二个参数上运行第一个和第二个指定函数。 >>>
然后将此箭头与进行比较的箭头组合在一起。
但正如我所说 - 写这篇文章可能没有任何理由。
这是基于箭头的解决方案,使用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
好了,我抬头一看Hoogle对于像一个在类型签名托马斯·荣格的 回答 ,并没有on
。 这是我搜索的内容:
(a -> b) -> (b -> b -> Bool) -> a -> a -> a
其中(a -> b)
相当于foo
, (b -> b -> Bool)
相当于<
。 不幸的是, on
的签名返回了其他内容:
(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
,与on
结合,非常接近你最初指定的,因为Ordering
有三个值 - 几乎是一个布尔值! :-)
所以,说你写on
的斯卡拉:
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")
使用currying和dot-free表示法真的更好。 :-)但是让我们试着用implicits:
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 (_ < _)
. on (_.length) select (_ < _)
更好。
这个表达式可以很优雅地写因子编程语言 -语言,其中的功能成分是做事情的方式,而且大多数代码写在卡点的方式。 堆栈语义和行多态有助于这种编程风格。 这就是您的问题的解决方案在因子中的样子:
# 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中看起来并不是特别优雅,但至少它向您展示了另一种做事方式。
使用on
和Monad
有一种很好的方法,但不幸的是,Scala在无点编程方面非常糟糕。 你的问题基本上是:“我可以减少这个程序中的积分数吗?”
想象一下,如果on
和if
是不同的咖喱和tupled:
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
Haskell的等价物是:
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.