14

私は、次のことを表現するためのより良い機能的な方法があるに違いないと推測しています。

def foo(i: Any) : Int

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

したがって、この例ではf == foop == _ < _。これには、scalazに巧妙な巧妙さが必要です。私は私が書くことができることを使用して見ることBooleanWができます:

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

しかし、私はabを一度だけ参照するコードを書くことができると確信していました。これが存在する場合、それは他のFunction1W何かの組み合わせにあるに違いありませんが、scalazは私には少し謎です!

編集私がここで求めているのは「どうやってこれを書くのか」ではないと思います。しかし、「そのような関数の正しい名前と署名は何ですか?それは、クライスリ、コモナドなど、私がまだ理解していないFPのものと関係がありますか?」

4

6 に答える 6

6

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
于 2010-02-19T09:27:28.553 に答える
5

ここでは、Arrowsやその他の特殊なタイプの計算が役立つとは思いません。結局のところ、通常の値で計算しているので、通常は純粋な計算を特別なタイプの計算(arr矢印またはreturnモナドに使用)に持ち上げることができます。

ただし、非常に単純な矢印の1つ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

***コンビネータは2つの入力を受け取り、最初と2番目の指定された関数をそれぞれ最初の2番目の引数で実行します。>>>次に、この矢印を比較を行う矢印で構成します。

しかし、私が言ったように、これを書く理由はおそらくまったくありません。

于 2010-02-19T13:42:03.137 に答える
4

これが、Scalazで実装されたArrowベースのソリューションです。これにはトランクが必要です。

昔ながらの関数で矢印の抽象化を使用しても大きなメリットはありませんが、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
于 2010-02-20T14:05:11.730 に答える
3

さて、Thomas Jungの回答にあるような型署名を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

これはほとんど同じですが、2つの場所でそれぞれとを置き換えるcと表示されます。Boola

ですから、今は存在しないのではないかと思います。より一般的な型アノテーションがあることに気付いたので、私もそれを試しました。

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

これは何ももたらさなかった。

編集:

今はそんなに遠くはなかったと思います。たとえば、次のことを考慮してください。

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

関数のmaximumByシグネチャはです。これは(a -> a -> Ordering) -> [a] -> a、と組み合わせると、 3つの値(ほぼブール値)がonあることを考えると、最初に指定したものにかなり近くなります。Ordering:-)

だから、あなたがonScalaで書いたとしましょう:

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 (_ < _)

于 2010-02-19T13:07:20.397 に答える
1

この式は、 Factorプログラミング言語(関数の合成が物事を行う方法であり、ほとんどのコードがポイントフリーの方法で記述されている言語)で非常にエレガントに記述できます。スタックセマンティクスと行多相は、このスタイルのプログラミングを容易にします。これは、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 ?

ここで私たちが興味を持っているのはコンビネータ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では特にエレガントに見えませんが、少なくとももう1つの方法を示しています。

于 2012-02-09T18:00:38.310 に答える
1

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
}

次に、リーダーモナドを使用できます。

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
于 2012-02-09T19:06:47.937 に答える