功能等效于IF(p(f(a),f(b))a erse b
-
21-09-2019 - |
题
我猜想必须有更好的功能表达以下方式:
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)
但是我确定我能够编写一些代码 仅参考 一个 和 b 一次. 。如果存在,则必须结合 Function1W
除了Scalaz以外,还有其他事情对我来说是个谜!
编辑: 我想我在这里问的不是“我该怎么写?”但是“这样的功能的正确名称和签名是什么,它与我尚不像Kleisli,Comonad等的FP内容有关吗?”
解决方案
以防万一它不在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
其他提示
我认为在这里箭头或任何其他特殊类型的计算都不是有用的。毕竟,您正在以正常值进行计算,通常可以举起 纯的 计算到特殊类型的计算(使用 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采用两个输入,并在第一个参数上分别在第一个参数上运行第一个和第二个指定的函数。 >>>
然后将此箭与确实比较的箭头组成。
但是正如我所说的 - 可能没有理由写这篇文章。
这是用Scalaz实现的基于箭头的解决方案。这需要树干。
通过使用普通的旧功能,将箭头抽象的抽象使用获得巨大的胜利,但这是在搬到Kleisli或Cokleisli Arrows之前学习它们的好方法。
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
好吧,我抬头 霍格 对于类型的签名,例如 托马斯·荣格(Thomas Jung) 回答, ,有 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
在Scala中:
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 (_ < _)
.
这个表达可以很好地写在 因素编程语言 - 功能组成的语言 这 做事的方式,大多数代码都是以无点的方式编写的。堆栈语义和行多态性促进了这种编程风格。这就是解决问题的方法:
# 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
被不同的咖喱和构成:
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