简体   繁体   English

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

[英]Functional equivalent of if (p(f(a), f(b)) a else b

I'm guessing that there must be a better functional way of expressing the following: 我猜测必须有更好的功能方式来表达以下内容:

def foo(i: Any) : Int

if (foo(a) < foo(b)) a else b 

So in this example f == foo and p == _ < _ . 所以在这个例子中f == foop == _ < _ There's bound to be some masterful cleverness in scalaz for this! 对于scalaz来说,必然会有一些熟练的聪明才智! I can see that using BooleanW I can write: 我可以看到使用BooleanW我可以写:

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

But I was sure that I would be able to write some code which only referred to a and b once . 但我确信我能够编写一些只引用ab一次的代码 If this exists it must be on some combination of Function1W and something else but scalaz is a bit of a mystery to me! 如果存在,它必须是Function1W和其他东西的某种组合,但scalaz对我来说有点神秘!

EDIT : I guess what I'm asking here is not "how do I write this?" 编辑我想我在这里问的不是“我怎么写这个?” but "What is the correct name and signature for such a function and does it have anything to do with FP stuff I do not yet understand like Kleisli, Comonad etc?" 但是“这个功能的正确名称和签名是什么?它与FP的东西有什么关系,我还不知道像Kleisli,Comonad等?”

Just in case it's not in Scalaz: 万一它不在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

Alternative with some overhead but nicer syntax. 替代一些开销但更好的语法。

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

I don't think that Arrows or any other special type of computation can be useful here. 我不认为Arrows或任何其他特殊类型的计算在这里有用。 Afterall, you're calculating with normal values and you can usually lift a pure computation that into the special type of computation (using arr for arrows or return for monads). 毕竟,你使用正常值进行计算,你通常可以将计算提升到特殊类型的计算中(使用arr表示箭头或return monad)。

However, one very simple arrow is arr ab is simply a function a -> b . 然而,一个非常简单的箭头是arr ab只是一个函数a -> b You could then use arrows to split your code into more primitive operations. 然后,您可以使用箭头将代码拆分为更原始的操作。 However, there is probably no reason for doing that and it only makes your code more complicated. 但是,可能没有理由这样做,它只会使您的代码更复杂。

You could for example lift the call to foo so that it is done separately from the comparison. 例如,您可以将调用解除为foo以便与比较分开完成。 Here is a simiple definition of arrows in F# - it declares *** and >>> arrow combinators and also arr for turning pure functions into arrows: 这是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)

Now you can write your code like this: 现在您可以像这样编写代码:

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

The *** combinator takes two inputs and runs the first and second specified function on the first, respectively second argument. ***组合器接受两个输入并在第一个和第二个参数上运行第一个和第二个指定函数。 >>> then composes this arrow with the one that does comparison. >>>然后将此箭头与进行比较的箭头组合在一起。

But as I said - there is probably no reason at all for writing this. 但正如我所说 - 写这篇文章可能没有任何理由。

Here's the Arrow based solution, implemented with Scalaz. 这是基于箭头的解决方案,使用Scalaz实现。 This requires trunk. 这需要后备箱。

You don't get a huge win from using the arrow abstraction with plain old functions, but it is a good way to learn them before moving to Kleisli or Cokleisli arrows. 使用带有普通旧函数的箭头抽象并没有获得巨大的胜利,但在移动到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

Well, I looked up Hoogle for a type signature like the one in Thomas Jung's answer , and there is on . 好了,我抬头一看Hoogle对于像一个在类型签名托马斯·荣格的 回答 ,并没有on This is what I searched for: 这是我搜索的内容:

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

Where (a -> b) is the equivalent of foo , (b -> b -> Bool) is the equivalent of < . 其中(a -> b)相当于foo(b -> b -> Bool)相当于< Unfortunately, the signature for on returns something else: 不幸的是, on的签名返回了其他内容:

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

This is almost the same, if you replace c with Bool and a in the two places it appears, respectively. 这几乎是一样的,如果你更换cBoola分别在它出现在两个地方。

So, right now, I suspect it doesn't exist. 所以,现在,我怀疑它不存在。 It occured to me that there's a more general type signature, so I tried it as well: 我发现有一个更通用的类型签名,所以我也尝试过:

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

This one yielded nothing. 这个没有产生任何结果。

EDIT: 编辑:

Now I don't think I was that far at all. 现在我觉得我没那么远。 Consider, for instance, this: 例如,考虑一下:

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

The function maximumBy signature is (a -> a -> Ordering) -> [a] -> a , which, combined with on , is pretty close to what you originally specified, given that Ordering is has three values -- almost a boolean! 函数maximumBy签名是(a -> a -> Ordering) -> [a] -> a ,与on结合,非常接近你最初指定的,因为Ordering有三个值 - 几乎是一个布尔值! :-) :-)

So, say you wrote on in Scala: 所以,说你写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))

The you could write select like this: 您可以像这样写select

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

And use it like this: 并像这样使用它:

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

Which really works better with currying and dot-free notation. 使用currying和dot-free表示法真的更好。 :-) But let's try it with implicits: :-)但是让我们试着用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 
  } 
}

Then you can write 然后你就可以写了

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

Very close. 很接近。 I haven't figured any way to remove the type qualifier from there, though I suspect it is possible. 我没想办法从那里删除类型限定符,虽然我怀疑它是可能的。 I mean, without going the way of Thomas answer. 我的意思是,没有采取托马斯回答的方式。 But maybe that is the way. 但也许就是这样。 In fact, I think on (_.length) select (_ < _) reads better than map (_.length) select (_ < _) . 实际上,我认为on (_.length) select (_ < _)读取比map (_.length) select (_ < _) . on (_.length) select (_ < _)更好。

This expression can be written very elegantly in Factor programming language - a language where function composition is the way of doing things, and most code is written in point-free manner. 这个表达式可以很优雅地写因子编程语言 -语言,其中的功能成分是做事情方式,而且大多数代码写在卡点的方式。 The stack semantics and row polymorphism facilitates this style of programming. 堆栈语义和行多态有助于这种编程风格。 This is what the solution to your problem will look like in Factor: 这就是您的问题的解决方案在因子中的样子:

# 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 ?

Of our interest here is the combinator 2keep . 我们感兴趣的是组合器2keep It is a "preserving dataflow-combinator", which means that it retains its inputs after the given function is performed on them. 它是一个“保留数据流 - 组合器”,这意味着它在对它们执行给定函数后保留其输入。


Let's try to translate (sort of) this solution to Scala. 让我们尝试将此解决方案转换为Scala。

First of all, we define an arity-2 preserving combinator. 首先,我们定义了一个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)

And an eagerIf combinator. 还有一个eagerIf组合者。 if being a control structure cannot be used in function composition; if是控制结构,则不能用于功能组合; hence this construct. 因此这个结构。

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

Also, the on combinator. 另外, on组合器。 Since it clashes with a method with the same name from Scalaz, I'll name it upon instead. 由于它与来自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]

And now put this machinery to use! 现在就把这个机器用掉了!

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)

This approach does not look particularly elegant in Scala, but at least it shows you one more way of doing things. 这种方法在Scala中看起来并不是特别优雅,但至少它向您展示了另一种做事方式。

There's a nice-ish way of doing this with on and Monad , but Scala is unfortunately very bad at point-free programming. 使用onMonad有一种很好的方法,但不幸的是,Scala在无点编程方面非常糟糕。 Your question is basically: "can I reduce the number of points in this program?" 你的问题基本上是:“我可以减少这个程序中的积分数吗?”

Imagine if on and if were differently curried and tupled: 想象一下,如果onif是不同的咖喱和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
}

Then you could use the reader monad: 然后你可以使用阅读器monad:

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

The Haskell equivalent would be: Haskell的等价物是:

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

Or... 要么...

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将F [A \\ / B]拆分为(F [A],F [B]) - How to split F[A \/ B] into (F[A], F[B]) Scala中f(a,b)和f(a)(b)之间的差异 - Difference between f(a,b) and f(a)(b) in Scala 如何将F [Either [A,B]]转换为Either [F [A],F [B]] - How to transform F[Either[A, B]] to Either[F[A], F[B]] 将&#39;A =&gt; F [G [B]]&#39;转换为&#39;F [G [A =&gt; B]&#39;标量 - transform 'A => F[G[B]]' into 'F[G[A=>B]' scala def compose [A,B,C](f:B =&gt; C,g:A =&gt; B):A =&gt; C = {f(g(_))}是不是有效的Scala声明? - def compose[A,B,C](f: B => C, g: A => B): A => C = {f(g(_))} is noty valid scala declaration? 电梯[A,B]中下划线的含义(f:A => B):选项[A] =>选项[B] = _映射f - Meaning of underscore in lift[A,B](f: A => B): Option[A] => Option[B] = _ map f `F[_ &lt;: A] &lt;: B` 在类型级别和 `f: A =&gt; B` 在值级别之间的类比 - Analogy between `F[_ <: A] <: B` at type-level and `f: A => B` at value-level 具有两个类型参数(即F [A,B]等于AFB)的仿制药的合成糖在哪里记录? - Where is synthetic sugar for generics with two type parameters (ie. that F[A,B] is equal to A F B) documented? 将Hlist [F [A]]转换为F [B],其中案例类B与A对齐类型 - Transforming a Hlist[F[A]] into F[B] where case class B has aligned types with A Scalaz monad变形金刚。 将f1:A =&gt; G [B],f2:B =&gt; G [C]函数应用于F [G [A]]对象 - Scalaz monad transformers. Applying f1:A => G[B], f2:B => G[C] function to F[G[A]] object
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM