2014-08-27 2 views
3

멀티 메소드에서 여러 메소드 간의 재구성을 재사용 할 수있는 방법이 있습니까?멀티 메소드의 재구성 사용

(defmulti foo (fn [x] (:a x))) 
(defmethod foo :1 [{:keys [a b c d e]}] (str a b c d e)) 
(defmethod foo :2 [a] "") 
(defmethod foo :3 [a] "") 

지금이 사소한 예이지만, 우리가 중첩 된지도와 훨씬 더 복잡 destructuring이 내가 foo는 내 모든 defmethods에 그것을 사용하고자하는 상상. 내가 어떻게 그럴 수 있니?

+0

Nit-picking : [this] (http://clojure.org/reader#The%20Reader--Reader%20forms)에 따르면 키워드는 문자 또는 *, +,!, - 등으로 시작한다고 가정합니다. _ 또는? – Thumbnail

답변

5

실용적인 솔루션은 각 개별 방법에 필요한 키만 사용하는 것입니다. 구조 조정에 대해주의해야 할 중요한 점은 구조 조정중인 컬렉션의 모든 값을 바인딩 할 필요가 없다는 것입니다. 이 멀티 메소드에 전달 된 모든 맵에 :a에서 :e까지의 키가 포함되어 있다고 가정 해 보겠습니다. 메소드 당 두 개의 키만 필요합니다. 당신이 뭔가를 할 수 있습니다 :

; note: a keyword can act as a function; :a here is equivalent to (fn [x] (:a x)) 
(defmulti foo :a) 
(defmethod foo :1 [{:keys [a b c d e]}] (str a b c d e)) 
(defmethod foo :2 [{:keys [b d]}] (str b d)) 
(defmethod foo :3 [{:keys [c e a]}] (str a c e)) 

당신은 복잡하게 중첩 된 구조를 가지고 있고, 당신은 당신의 사용 사례에 따라 대안으로 필요하지 않은 키, 또는을 남길 수 있습니다 특정 값을 잡아하려면 함수 정의 내에서 let 바인딩이 더 읽기 쉬워 질 수 있습니다. 스티브 로쉬 (Steve Losh)의 Caves of Clojure이 마음에 듭니다. Clojure에서 처음부터 로구아나 (roguelike) 텍스트 어드벤처 게임을 작성하면서 중첩 된 맵을 사용하여 게임의 상태를 나타 냈습니다. 처음에 그는 "게임 상태"지도의 내부 비트에 액세스 할 수 destructuring를 사용하여 기능, 예를 들어, 일부 썼다 :

(defmethod draw-ui :play [ui {{:keys [tiles]} :world :as game} screen] 
    ... 

그러나 later을, 그는에 밖으로 destructuring를 당겨이 코드를 읽기 쉽게하기로 결정 바인딩하자 : 당신이 중첩 구조로 작업하는 경우

(defmethod draw-ui :play [ui game screen] 
    (let [world (:world game) 
     tiles (:tiles world) 
     ... 

요점은, 그리고 당신은 (간단한 코드를 유지하려는 당신은으로 동일한 구조를 가지고 여러 가지 방법과 multimethod을 쓰고있어 특히 인수), 당신은 파괴를 사용하지 않고 단지 let 바인딩을 사용하여 당신이 원하는 조각. get-in은 중첩 된 컬렉션에서 값을 간단히 가져 오는 데 유용한 도구입니다. 스티브 그냥 타일을 필요한 경우 Clojure의 예제의 동굴로 돌아가서, 그는이 같은 것을 할 수 있었다 :

(defmethod draw-ui :play [ui game screen] 
    (let [tiles (get-in game [:world :tiles]) 
    ... 

개인적으로, 나는 훨씬 쉽게이 {{:keys [tiles]} :world :as game}와 함수 인수를 일 처리보다 읽는 것을 찾을 수 있습니다.


편집 :

당신이 정말 각 multimethod의 destructuring을 반복하는 것을 피하려고, 당신은 매크로를 쓸 수, 각 방법을 사용할 수있는 동일한 바인딩을 갖고 싶어 :

(defmulti foo :a) 

(defmacro deffoomethod [dispatch-val & body] 
    `(defmethod foo ~dispatch-val [{:keys [~'a ~'b ~'c ~'d ~'e]}] 
    [email protected])) 

(deffoomethod 1 (str a b c d e)) 
(deffoomethod 2 (str b d)) 
(deffoomethod 3 (str a c e)) 

(foo {:a 1 :b 2 :c 3 :d 4 :e 5}) 
;=> "12345" 

(foo {:a 2 :b \h :d \i}) 
;=> "hi" 

(foo {:a 3 :b \x :c 0 :d \x :e 0}) 
;=> "300" 

매크로 위생을 손상 시키므로이 방법은 권장하지 않습니다. 이 매크로를 사용하는 사람은 누구나 a부터 e까지의 기호를 해당 인수의 키와 바인드한다는 것을 기억해야합니다. 이는 문제가 될 수 있습니다.

+0

굉장! 감사! – user3748315

+0

@Dave - 좋은 글을 올리세요! –