Domanda

Immagino che ci debba essere un modo migliore funzionale per esprimere quanto segue:

def foo(i: Any) : Int

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

Quindi in questo esempio f == foo e p == _ < _. Ci sarà sicuramente un po 'di intelligenza magistrale in Scalaz per questo! Lo vedo usando BooleanW Posso scrivere:

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

Ma ero sicuro che sarei stato in grado di scrivere un po 'di codice che solo riferito a un e b una volta. Se questo esiste deve essere in una combinazione di Function1W E qualcos'altro ma Scalaz è un po 'un mistero per me!

MODIFICARE: Immagino che quello che sto chiedendo qui non sia "Come scrivo questo?" Ma "Qual è il nome e la firma corretti per una tale funzione e ha qualcosa a che fare con le cose FP che non capisco ancora come Kleisli, Comonad ecc.?"

È stato utile?

Soluzione

Nel caso in cui non sia in 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

Alternativa con un po 'di sintassi sopraelevata ma più bella.

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

Altri suggerimenti

Non penso che le frecce o qualsiasi altro tipo di calcolo speciale possano essere utili qui. Dopotutto, stai calcolando con valori normali e di solito puoi sollevare a puro calcolo nel tipo speciale di calcolo (usando arr per frecce o return per le monadi).

Tuttavia, una freccia molto semplice è arr a b è semplicemente una funzione a -> b. È quindi possibile utilizzare le frecce per dividere il codice in operazioni più primitive. Tuttavia, probabilmente non c'è motivo per farlo e rende solo il tuo codice più complicato.

Potresti ad esempio sollevare la chiamata a foo in modo che sia fatto separatamente dal confronto. Ecco una definizione Simiple di frecce in f# - dichiara *** e >>> Combinatori freccia e anche arr Per trasformare le funzioni pure in frecce:

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)

Ora puoi scrivere il tuo codice in questo modo:

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

Il *** Combinator prende due input ed esegue la prima e la seconda funzione specificata nel primo, rispettivamente secondo argomento. >>> Quindi compone questa freccia con quella che fa il confronto.

Ma come ho detto, probabilmente non c'è alcun motivo per aver scritto questo.

Ecco la soluzione basata sulla freccia, implementata con Scalaz. Questo richiede tronco.

Non ottieni una grande vittoria dall'uso dell'astrazione di freccia con semplici funzioni, ma è un buon modo per impararle prima di trasferirsi alle frecce di Kleisli o 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

Bene, ho alzato lo sguardo Hoogle per una firma di tipo come quella in Thomas Jung's Rispondere, e c'è on. Questo è ciò che ho cercato:

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

Dove (a -> b) è l'equivalente di foo, (b -> b -> Bool) è l'equivalente di <. Sfortunatamente, la firma per on Restituisce qualcos'altro:

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

Questo è quasi lo stesso, se si sostituisce c insieme a Bool e a Nei due luoghi appare, rispettivamente.

Quindi, in questo momento, sospetto che non esista. Mi è venuto in mente che esiste una firma di tipo più generale, quindi l'ho provato anche:

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

Questo non ha prodotto nulla.

MODIFICARE:

Ora non credo di essere stato così lontano. Considera, ad esempio, questo:

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

La funzione maximumBy La firma è (a -> a -> Ordering) -> [a] -> a, che, combinato con on, è abbastanza vicino a quello che hai originariamente specificato, dato che Ordering ha tre valori - quasi un booleano! :-)

Quindi, dì che hai scritto on In 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))

Tu potresti scrivere select come questo:

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

E usalo in questo modo:

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

Il che funziona davvero meglio con la notazione curry e senza punte. :-) Ma proviamolo con implicit:

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 
  } 
}

Quindi puoi scrivere

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

Molto vicino. Non ho pensato a nessun modo per rimuovere il qualificatore di tipo da lì, anche se sospetto che sia possibile. Voglio dire, senza percorrere la risposta di Thomas. Ma forse quello è il modo. In effetti, penso on (_.length) select (_ < _) legge meglio di map (_.length) select (_ < _).

Questa espressione può essere scritta in modo molto elegante in Linguaggio di programmazione dei fattori - Una lingua in cui la composizione della funzione è il Il modo di fare le cose e la maggior parte del codice è scritta in modo senza punto. La semantica dello stack e il polimorfismo delle righe facilita questo stile di programmazione. Questo è l'aspetto della soluzione al tuo problema nel fattore:

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

Del nostro interesse qui è il combinatori 2keep. È un "preservare il compensazione del flusso di dati", il che significa che mantiene i suoi input dopo che la funzione data è stata eseguita su di essi.


Proviamo a tradurre (una specie di) questa soluzione in Scala.

Prima di tutto, definiamo un combinatori di Arity-2 che preserva.

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)

E un eagerIf Combinator. if Essere una struttura di controllo non può essere utilizzato nella composizione della funzione; Da qui questo costrutto.

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

Anche il on Combinator. Dal momento che si scontra con un metodo con lo stesso nome di Scalaz, lo chiamerò upon invece.

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]

E ora metti da usare questo macchinario!

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)

Questo approccio non sembra particolarmente elegante in Scala, ma almeno ti mostra un altro modo di fare le cose.

C'è un modo piacevole per farlo con questo on e Monad, ma Scala è purtroppo molto negativa nella programmazione senza punti. La tua domanda è fondamentalmente: "Posso ridurre il numero di punti in questo programma?"

Immagina se on e if erano diversamente curie e tuplicati:

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
}

Quindi potresti usare il lettore monade:

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

L'equivalente di Haskell sarebbe:

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

O...

flip =<< flip =<< (if' .) . on (<) f
  where if' x y z = if x then y else z
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top