필자는 skuro가 무엇이 잘못되었는지와 왜, 그리고 원래 목표를 해결하는 데 사용할 수있는 매우 유용한 라이브러리를 제안했는지에 대한 좋은 답을 제공한다고 생각합니다. "왜 내가 이것을 읽는거야?" 나는 당신이 묻는 것을 듣는다. 무서운 거시적 땅에 들어가는 일없이 몇 줄의 코드만으로 데카르트 제품에 함수를 적용하는 것을 일반화 할 수있는 방법을 공유하는 것이 재미있을 것이라고 생각했습니다.내가 일반화 말할 때
, 내가 목표는 같은 일을 할 수있을 것이라고 의미 : 우리는이 그리 멀리 존재 교환하다 기능을 개발하는 경우,
user> (permute str ["abc" "ab"])
=> ("aa" "ab" "ba" "bb" "ca" "cb")
좋은 점입니다
user> (permute + [[1 2 3] [10 20 30]])
=> (11 21 31 12 22 32 13 23 33)
이것은 장난감의 예이지만이 방법으로 일반화하면 얻을 수있는 유연성을 전달할 수 있습니다.
(defn permute [f [coll & remaining]]
(if (nil? remaining)
(map f coll)
(mapcat #(permute (partial f %) remaining) coll)))
나는 결합하는 다른 문자열만큼의 반복에 대한 map
또는 mapcat
과 반복 된 시작 핵심 아이디어 :
음, 여기에 내가 생각 해낸 아주 간결한 방법입니다. 나는 당신의 예를 시작하고, "상세"비 일반적인 솔루션 썼다 :
user> (mapcat (fn [i] (map (partial str i) "ab")) "abc")
=> ("aa" "ab" "ba" "bb" "ca" "cb")
이 mapcat
아래 "abc"
기능들. 구체적으로는, 의 "abc"
이 s이고, 이때 사용하는 단일 요소는 "abc"
(i
)이고, 각 요소는 "ab"
입니다.
이것을 함수로 일반화하는 방법을 이해하려면 하나의 "레벨을 더 깊게"이동시켜야하고 세 번째 문자열로 시도해야합니다. 이 mapcat
user> (mapcat (fn [i] (mapcat (fn [j] (map (partial str i j) "def")) "ab")) "abc")
=> ("aad" "aae" "aaf" "abd" "abe" "abf" "bad" "bae" "baf" "bbd" "bbe" "bbf" "cad" "cae" "caf" "cbd" "cbe" "cbf")
는 mapcat
가 map
S str
함께 조합 적 방법으로 소자를 보내고 한창 것을 함수 S는 것을 함수이야. 휴. 이제 나는 내가 어떻게 일반화 할 수 있는지보기 시작했다. 가장 안쪽의 표현은 항상 map
일부 일종의 부분 str
문자열의 목록에서 마지막 문자열을 다시 조합하여 다시 조합합니다. 바깥 쪽 표현식은 단지 mapcat
이고, 가장 앞쪽에있는 문자열은 가장 바깥 쪽 문자열이 다시 묶는 문자열 목록의 첫 번째 문자열을 사용하는 지점까지 이어집니다.
여기에서 나는 전체 부분 str
을 한 번에 정의 할 필요가 없다는 것을 알았지 만 재귀 적으로 permute
이라고 불렀기 때문에 "구축 할"수있었습니다.
필자는 이제 어떻게 함수가 작동하는지 설명 할 수있는 충분한 컨텍스트를 제공했으면 좋겠다. permute
의 마지막 반복은 남아있는 콜이없는 경우 (즉, (nil? remaining)
이 true
을 반환) 발생합니다. 그것은 단지 map
의 기능을 제공합니다 그것은 마지막 coll 아래로 주어진 것입니다.
콜이 남아있는 경우 mapcat
permute
변형을 현재 콜에서 내립니다. 이 순열 변종은 익명의 인수와 함께 f
의 부분 함수를 사용하고 나머지 colls는 permute
입니다. 이렇게하면 부분적으로 함수를 점증 적으로 생성하여 최종적으로 콜 목록 끝에 도달하면 호출됩니다. 내 머리 속에서, 역 추적을 상상해 보았습니다. 결과적으로 다시 결합 된 콜과 함께 해체 될 때까지 중첩 된 mapcat
을 호출했습니다.
이 기능은 비록 간결하지만 아마도 최적이라고 생각하지 않습니다.솔직히 CS 배경이별로 없지만 Clojure에 대한 정보를 얻으려는 시점에서 self-recursive calls 대신 loop
/recur
을 사용하는 것이 훨씬 "효율적"인 경향이 있습니다. 최적화가 중요하다면 loop
/recur
을 사용하는 함수를 다시 작업하는 것이 상당히 간단하다고 상상해보십시오.
매크로가 아닌 함수가 필요합니다. 또는 eval을 사용하십시오 : (eval'(make-strings ~ sy)) – Ankur
매크로 인자에 하드 데이터와 var를 전달하는 것과 다른 점이 있습니다. 감사. 매크로 루트를 사용하는 것은 나의 첫 번째 선택이 아니었지만 성공을 거두었지만 매우 제한적이었습니다. – Brian