2012-03-16 1 views
8

두 개의 목록이있는 특성이 있다고 가정 해보십시오. 가끔은 그 중 하나에, 때로는 다른 사람들에게도 관심이 있습니다.상태 선택 컨텍스트를 호출 스택에 전달하는 것을 피하는 "기능적 방법"은 무엇입니까?

trait ListHolder { 
    val listOne = List("foo", "bar") 
    val listTwo = List("bat", "baz") 
} 

나는 내가 목록 사이에서 선택을해야하는 상황이있는 상단에, 함수 호출의 체인을 가지고 있지만, 하단에있는 나는 특성을 사용합니다. 명령형 패러다임

, I는 기능을 통해 컨텍스트를 건네 객체 지향 패러다임
class Imperative extends Object with ListHolder { 
    def activeList(choice : Int) : List[String] = { 
    choice match { 
     case 1 => listOne 
     case 2 => listTwo 
    } 
    } 
} 

def iTop(is : List[Imperative], choice : Int) = { 
    is.map{iMiddle(_, choice)} 
} 

def iMiddle(i : Imperative, choice : Int) = { 
    iBottom(i, choice) 
} 

def iBottom(i : Imperative, choice : Int) = { 
    i.activeList(choice) 
} 

val ps = List(new Imperative, new Imperative) 
println(iTop(ps, 1)) //Prints "foo, bar" "foo,bar" 
println(iTop(ps, 2)) //Prints "bat, baz" "bat, baz" 

는 I 컨텍스트를 전달 피하기 위해 내부 상태를 사용할 수

class ObjectOriented extends Imperative { 
    var variable = listOne 
} 

def oTop(ps : List[ObjectOriented], choice : Int) = { 
    ps.map{ p => p.variable = p.activeList(choice) } 
    oMiddle(ps) 
} 

def oMiddle(ps : List[ObjectOriented]) = oBottom(ps) 

def oBottom(ps : List[ObjectOriented]) = { 
    ps.map(_.variable) //No explicitly-passed-down choice, but hidden state 
} 

val oops = List(new ObjectOriented, new ObjectOriented) 

println(oTop(oops, 1)) 
println(oTop(oops, 2)) 

함수 언어로 비슷한 결과를 얻는 관용적 인 방법은 무엇입니까?

즉, 위의 결과와 비슷한 출력을 보내고 싶습니다.

class Functional extends Object with ListHolder{ 
    //IDIOMATIC FUNCTIONAL CODE 
} 

def fTop(fs : List[Functional], choice : Int) = { 
    //CODE NEEDED HERE TO CHOOSE LIST 
    fMiddle(fs) 
} 

def fMiddle(fs : List[Functional]) = { 
    //NO CHANGES ALLOWED 
    fBottom(fs) 
} 

def fBottom(fs : List[Functional]) = { 
    fs.map(_.activeList) //or similarly simple 
} 

def fs = List(new Functional, new Functional) 

println(fTop(fs, 1)) 
println(fTop(fs, 2)) 

업데이트 : 이것이 제대로 작동한다고 생각 되나요?

class Functional extends Imperative with ListHolder{} 

class FunctionalWithList(val activeList : List[String]) extends Functional{} 

def fTop(fs : List[Functional], band : Int) = { 
    fMiddle(fs.map(f => new FunctionalWithList(f.activeList(band)))) 
} 

def fMiddle(fs : List[FunctionalWithList]) = { 
    //NO CHANGES ALLOWED 
    fBottom(fs) 
} 

def fBottom(fs : List[FunctionalWithList]) = { 
    fs.map(_.activeList) 
} 

def fs = List(new Functional, new Functional) 

println(fTop(fs, 1)) 
println(fTop(fs, 2)) 
+0

작은 메모 : 'ListHolder와 함께 extends 객체'를 쓸 필요가 없습니다. 'extends ListHolder'라고 써라. (클래스는 특성을 확장 할 수있다.) – Jesper

답변

5

글쎄, 이런 종류의 문제를 처리하기 위해 항상 모나드와 모나드를 사용할 수 있지만 문제의 핵심은 스택 아래로 선택 사항을 전달하는 대신 스택을 통해 함수를 반환한다는 것입니다. 어떻게 문제를 해결할 수 있는지 알고 있습니다.

def fTop(fs : List[Functional]) = { 
    fMiddle(fs) 
} 

def fMiddle(fs : List[Functional]) = { 
    fBottom(fs) 
} 

def fBottom(fs : List[Functional]) = { 
(choice: Int) => fs map (_ activeList choice) 
} 

그리고

println(fTop(fs)(1)) 

당신이 이런 종류의 일에 대한 패턴을 개발하기 시작하면, 당신은 모든 종류의 모나드 (모나드의 각 종류는 특정 패턴을 나타냄)와 끝까지.

+0

여기에 모나드를 실제로 권장하지는 않지만 그 코드는 너무 우아해서 +1을해야합니다 ... – Owen

+0

"문제의 핵심은 스택 아래로 선택 사항을 전달하는 대신 스택 위로 함수를 반환한다는 것입니다." 나는 이것이 나의 오래된 OOP 마음이 몇 번이고 그리워한다는 핵심 개념이라고 생각한다. –

+0

@LarryOBrien 코드를 두 번 작성해야 제대로 할 수있었습니다. 외국어와 비슷합니다. 말하기보다는 외국어로 번역하는 것이 훨씬 어렵습니다. –

1

첫 번째 긴급한 버전이 가장 기능적으로 보입니다.

일반적으로 리더 모나드와 상태 모나드 변환기는 컨텍스트 또는 상태를 호출 스택에 전달하는 데 사용됩니다.

마찬가지로 상태 모나드의 경우 Scalaz state monad examples을 참조하고 유사한 질문과 답변을 보려면 scalaz 우편물 list thread을 참조하십시오.

2

"업데이트"아래의 답변이 완벽하다고 생각합니다. 예, 여기에서 Reader 모나드를 사용할 수 있습니다. 그러나 왜 모나드를 사용하지 않는 완벽하게 좋은 솔루션을 가지고있을 때 귀찮은가요?

Daniel의 모나드 솔루션은 아름답고 우아하지만, fTopfMiddle 메서드가 더 복잡해지기 시작하면 누락 된 매개 변수를 "건너 뛰기"위해 많은 추가 구문이 필요합니다.

내가 컨텍스트를 저장하는 class를 사용하여 생각하는 것이 적절한 이유는 기능 사이의 컨텍스트를 공유 : 클래스가 무엇인지의

  • .

  • 스칼라는 모나드보다 클래스에 대해 훨씬 멋진 구문을 사용합니다.