2012-07-11 3 views
2

저는 스칼라에 대해 매우 익숙하며 문제가 발생했습니다.함수를 스칼라 함수의 반환 형식으로 할당하는 방법

함수 대기열을 보유 할 클래스를 작성하려고합니다. 큐에 함수를 추가하고 모든 함수가 추가되면이를 실행할 수 있기를 원합니다. 기본적으로 "function1 (function2 (function3()))"과 같은 표현식을 작성하여 리턴하고 평가합니다. 이것은 내가 지금까지 가지고있는 코드입니다 :

class Pipeline() { 

    // Queue of functions to run 
    private var queue: Queue[ _ => _] = new LinkedList(); 

    // Add functions to the queue 
    def addFunction(func:_ => _): Unit ={ 
     queue.add(func) 
    } 

    // Run all the functions in the queue 
    def run(): Unit = { 
     val function = runHelper(queue.poll(), queue) 
     function 
    } 

    def runHelper(func: _ => _, queue: Queue[_ => _]): _ = { 
     // Recursion base case 
     if(queue.isEmpty) 
     return func 
     // Keep building the function recursively 
     else 
     func(runHelper(queue.poll(), queue))   
    }  
    } 

나는 여기에 하나 이상의 오류가 있다고 확신합니다. 하지만 지금 당장 멈춰야 할 것은 runHelper 함수의 반환 유형입니다. 볼 수 있듯이 _ 와일드 카드를 사용하려고하지만 컴파일 오류가 발생합니다. 함수가 함수를 반환한다고 어떻게 정의 할 수 있습니까? 그리고 나는 이것을 좋은 방향으로 가고 있습니다 - 그렇지 않다면 문제에 대한 더 적절한 해결책의 방향으로 나를 가리켜주십시오.

edit1 : 설명 함수의 입력 및 반환 유형은 미리 알지 못하며 함수 순서는 동적으로 할당 할 수 있어야합니다.

Edit2가 : 나는 Edmondo1984 내가 원하는 방식으로 일을 제안 코드를 얻기 위해 노력했습니다,하지만 난 그것을 얻을 수없는 것 더 문제. 내가 특별히 알아야 할

val func1: String => File = function1 
val func2: File => File = function2 

var queue = func1 

if(runFunc2) 
    queue = queue :: func2 

queue("exampleString") 

는 "큐 = 큐 :: FUNC2"을 할 수 있도록하는 방법입니다 : 내가해야 할 일은

은 다음과 같이 될 것이다. ::가 FunctionQueue를 반환하기 때문에 큐 변수에 할당 할 수있을 것이라고 상상했을 것입니다. 그러나 다시 한번, 변수의 첫 번째 초기화 이후 "String => File"요구가 발생합니다. 나는 내 머리 위로 조금이라도있는 것처럼 느껴진다. 그리고 어떤 도움이라도 대단히 감사 할 것이다.

+0

사전에 알려진 함수 시퀀스가 ​​동적으로 생성 되었습니까? 예전의 경우 얀의 대답은 갈 길입니다. –

+0

시퀀스가 ​​동적으로 생성됩니다. 그래서 지금 당장은 Edmondo1984 대답이 갈 길인 것 같아요. 그러나 나는 아직도 그것에 대해 내 마음을 감싸려고 노력하고있다. – Johan

+0

이들은 기능이 있습니까? 즉, 입력 유형이 반환 유형과 같으므로 모든 함수가 동일한 유형을 공유합니까? –

답변

3

_은 실존 유형의 자리 표시 자이므로 불가능합니다. 현재 존재하지만 관련이없는 유형입니다. 예를 들어 다음과 같은 목록을 인쇄 할 때 사용할 수 있습니다.

scala> def aMethod(a:List[_]) = println(a.size) 
aMethod: (a: List[_])Unit 

scala> val b = List(2,3,4) 
b: List[Int] = List(2, 3, 4) 

scala> aMethod(b) 
3 

실제로이 요소는 목록의 요소에 액세스하지 않기 때문에 작동합니다. 따라서 클래스가 필요 없다고 생각할 수 있습니다.

스칼라는 강력한 형식의 언어이므로 컴파일러는 코드에서 서명이 존중되는지 확인합니다. 대기열 [_ => _]은 알 수없는 유형에서 알 수없는 유형까지의 함수 대기열이며 유용하지 않습니다. 왜냐하면 함수를 팝업하여 입력 매개 변수에 적용하려고하면 해당 입력 매개 변수가 서명과 일치하는지 확인하십시오.

당신이하려는 것은 사소한 것이 아니며 재귀 적으로 큐를 정의해야합니다. 당신은 볼품 HLIST 구현을 읽을 수 있습니다,하지만 아이디어는 다음과 같다 :

trait FunctionQueue[A,B]{ 

    def ::[C](newFunction: C => A): FunctionQueue[C,B] 

    def apply(a:A):B 
} 

class FunctionQueueImpl[A,B,C](val f:A=>B, val queue:FunctionQueue[B,C]) extends FunctionQueue[A,C]{ 

    def apply(a:A) = queue.apply(f(a)) 

    def ::[D](newFunction: (D) => A):FunctionQueue[D,C] = new FunctionQueueImpl[D,A,C](newFunction,this) 
} 

object FunctionQueue { 
    def EmptyQueue[T]:FunctionQueue[T,T] = new FunctionQueue[T,T] { 
    def ::[C](newFunction: (C) => T):FunctionQueue[C,T] = new FunctionQueueImpl[C,T,T](newFunction,this) 

    def apply(a: T):T = a 
    } 

    implicit def functionToQueue[A,B](function:A => B):FunctionQueue[A,B] = new FunctionQueueImpl(function, EmptyQueue[B]) 
} 

는 이제 REPL에서 그것을 시도 할 수 있습니다 :

scala> import FunctionQueue._ 
import FunctionQueue._ 

scala> val a: Int => Int = _ * 10 
a: Int => Int = <function1> 


scala> val b: Double => Int = _.toInt 
b: Double => Int = <function1> 

scala> val c : String => Double = _.toDouble 
c: String => Double = <function1> 

scala> val queue = c::b::a 
queue: FunctionQueue[String,Int] = [email protected] 

scala> queue("1.25") 
res1: Int = 10 

scala> queue("3.25") 
res2: Int = 30 
내가 마일 Sabins 더 이해하기 위해 노력 읽는 제안

.

+2

좋습니다.하지만 함수의 순서가 앞쪽에 있다는 것을 알면, 예제에서와 같이 작성 (compose b compose c) ("1.25")을보다 쉽게 ​​작성할 수 있습니다. 엉성한 것만이 유일한 망치는 아닙니다 ;-) –

+0

이것은 정확하게 내가하려고했던 것입니다. 고마워요! – Johan

+0

나는 이전의 진술에 틀린 것처럼 보입니다. 나는 아직도 내가 원하는 것을 어떻게하는지 이해하지 못한다. 자세한 내용은 질문을 참조하십시오. – Johan

0

당신과 같이 함수의 유형을 지정할 수 있습니다

Function[T, T] 

또는 함수의 입력 및 출력을 가정

T => T 

은 정말해야하는 동일하다. 물론 T을 지정하거나 바꿔야합니다.

가변 입력 - 출력이있는 경우 전체 구성표가 훨씬 복잡해집니다.

+0

이것이 문제입니다. 함수의 입력 및 출력 유형이 다를 수 있습니다. 이것을 달성 할 방법이 있습니까? 나의 직감은 이러한 목적으로 와일드 카드를 사용하는 것이었지만 이것이 효과가없는 것 같습니다. – Johan

+0

불변 구조와 구성으로 갈 것입니다 ('compose' 또는'andThen' 사용). – Debilski

3

내가 선호하는 형식은 (parameter list) => returnType입니다.

다음은 귀하의 코드가 어떻게 보이는지입니다. 내 IDE가 좋아하지만 보장은 없습니다.

class Pipeline[T]() { 

// Queue of functions to run 
private var queue: util.Queue[ (T) => T] = new util.LinkedList() 

// Add functions to the queue 
def addFunction(func: (T)=> T) { 
    queue.add(func) 
} 

// Run all the functions in the queue 
def run() { 
    val function = runHelper(queue.poll(), queue) 
    function 
} 

def runHelper(func: (T) => T, queue: util.Queue[(T)=> T ]): (T)=>T = { 
    // Recursion base case 
    if(queue.isEmpty) 
    func 
    // Keep building the function recursively 
    else 
    func compose runHelper(queue.poll(), queue) 
    } 
} 
+0

이 코드를 사용하면 새로운 Pipeline 인스턴스를 선언 할 때 T 유형을 설정했을 것입니다. 그러나 나는 다른 기능을 위해 다른 입력과 출력을 갖기 때문에 나에게 도움이되지 못할 것이다. 그래도 스칼라를 이해하는 데 도움이됩니다. :) – Johan

+0

스칼라는 typesafe이므로 얻을 수있는 방법에 한계가 있음을 기억하십시오. 예를 들어 하나의 함수 (Int) => String with (String) => Double과 같이 작성할 수있는 파이프 라인을 만들 수 있다는 것을 나는 모른다. 네, 출력은 다른 출력의 입력이기 때문에 구성 가능 합니다만,'파이프 라인 '과 비슷한 것을 사용하여 출력 할 수는 없습니다. –

+0

"하지만 당신은 파이프 라인과 비슷한 것을 사용하여 그렇게 할 수 없을 것입니다."- 나는 당신이 절대적으로 바로 그 것입니다. 현재 나의 추측은 @ Edmondo1984가 제시 한 답변을 통해 무엇인가를해야한다는 것입니다. 시간을내어 주셔서 대단히 감사합니다.나는 당신을 투표 할 것이지만,할만한 담당자가 없어서 두려워합니다. – Johan

3

기능 구성을 시도해 볼 수 있습니다. 예를 들면 다음과 같습니다.

val f = (x:Int) => 2 * x 
val g = f.compose(f) 

in 및 출력 유형이 다를 경우 출력을 일치시키고 처리해야합니다.

1

구현에 관해서는 T => T (endofunctions라고 함)의 간단한 List을 사용할 수 있습니다.

이제 scalaz에서 이러한 기능을 위해 Monoid (SemigroupZero에서 빌드 됨) 인스턴스가 있습니다. 즉, 다음 코드를 작성할 수 있습니다.

scala> import scalaz._, Scalaz._ 
import scalaz._ 
import Scalaz._ 

scala> val f = ((_:Int) + 6).endo 
f: scalaz.Endo[Int] = <function1> 

scala> val g = ((_:Int) * 4).endo 
g: scalaz.Endo[Int] = <function1> 

scala> val h = (-(_:Int)).endo 
h: scalaz.Endo[Int] = <function1> 

scala> f :: g :: h :: nil 
res3: List[scalaz.Endo[Int]] = List(<function1>, <function1>, <function1>) 

scala> .asMA.sum 
res4: scalaz.Endo[Int] = <function1> 

scala> f(g(h(1))) 
res5: Int = 2 

scala> res4(1) 
res6: Int = 2 
관련 문제