機能性と同等の場合p(f(a)、f(b))aも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)
できるという確信があった私は書くことができる一部のコードする みと 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
他のヒント
ここでは、矢印やその他の特別なタイプの計算が役立つとは思いません。結局のところ、あなたは通常の値で計算しており、通常は ピュア 特別なタイプの計算に計算する(使用 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
***
Combinatorは2つの入力を取得し、それぞれ最初の、2番目の引数で1番目と2番目の指定関数を実行します。 >>>
次に、この矢を比較する矢印で構成します。
しかし、私が言ったように - これを書く理由はまったくありません。
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 aタイプシグニチャーのような トーマスチョンの 答え, あり 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
はつの値とほぼboolean!:-)
でも書いた 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")
どんどん作品をより良いcurryingド-無料表記です。●:-)がやってみましょうと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 (_ < _)
.
この表現は非常にエレガントに書くことができます ファクタープログラミング言語 - 関数構成がある言語 物事を行う方法、およびほとんどのコードはポイントフリーの方法で記述されます。スタックセマンティクスと列の多型は、このスタイルのプログラミングを促進します。これが、あなたの問題の解決策が要因のように見えるものです。
# 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つの方法を示しています。
これを行うための素敵な方法があります 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