2017-05-15 5 views
6

저는 Clojure 매크로를 배우고 있으며 왜 메타 프로그래밍을위한 함수를 사용할 수 없는지 궁금합니다.Clojure, 매크로로 함수로 할 수없는 것을 할 수 있습니까?

지금까지 내가 반환 값 반면 매크로가 호출되는 곳에서 (평가, 그들은이 같은 매크로 기능의 차이는 매크로의 인수를 평가하지만, 데이터 구조 및 기호로 전달되지 않는 것입니다 알고). 매크로는 독자와 평가자 간의 프록시로 작동하여 평가가 수행되기 전에 임의의 방식으로 양식을 변형합니다. 내부적으로 함수, 특수 형식, 리터럴, 재귀, 기타 매크로 등 모든 언어 기능을 사용할 수 있습니다.

기능이 반대입니다. 인수는 호출 전에 평가되고 리턴 값은 리턴 후에 나오지 않습니다. 그러나 매크로와 함수의 미러링 특성 때문에 저의 궁금증을 유발할 수 있습니다. 매크로를 매크로로 사용하여 인수 (폼)를 인용하고 폼을 변환하고 함수 내에서 평가 한 다음 최종적으로 값을 반환합니다. 이것은 논리적으로 같은 결과를 가져 오지 않을까요? 물론 이것은 불편할 것이지만 이론적으로는 이 모든 가능한 매크로에 대해 동등한 기능을 가지고 있습니다? 여기

여기
(defmacro infix 
    "translate infix notation to clojure form" 
    [form] 
    (list (second form) (first form) (last form))) 

(infix (6 + 6)) ;-> 12 

이 같은 논리는

(defn infix-fn 
    "infix using a function" 
    [form] 
    ((eval (second form)) (eval (first form)) (eval (last form)))) 

(infix-fn '(6 + 6)) ;-> 12 

이제 함수를 사용하는 매크로 간단한 중위이며, '모든 상황에 일반화 이러한 인식은, 또는 일부 코너의 경우 매크로 나오지 않았어가있다 능가 할 수 없습니까? 결국 매크로는 함수 호출에 대한 통사론적인 설탕입니까?

+3

매크로는 런타임이 아닌 매크로 확장 (일반적으로 컴파일하기 전에) 중에 재귀 적으로 확장됩니다. 컴파일 된 코드는 확장을 직접 작성한 것처럼 보이므로 성능상의 불이익은 없습니다. 또한'eval'은 null 어휘 환경에서 폼을 평가한다는 것을 기억하십시오. 컴파일러 ... 심볼을 해석 할 수 없습니다 : x ' – jkiiski

+0

이것들은 제가 생각하지 못한 좋은 점입니다 –

답변

11

답하기 전에 질문을 읽으면 도움이됩니다.

귀하의 중위 기능은 리터럴를 제외하고 작동하지 않습니다

(let [m 3, n 22] (infix-fn '(m + n))) 
CompilerException java.lang.RuntimeException: 
Unable to resolve symbol: m in this context ... 

이 @jkinski는 지적 무엇의 결과이다 : eval 행위, m가 없어 시간에 의해.


매크로는 어떤 기능을 할 수 없습니다 할 수 있습니까?

예. 그러나 당신이 기능으로 그것을 할 수 있다면, 당신은 일반적으로해야합니다.

매크로

  • 연기 평가를 위해 좋은;
  • 캡처 양식;
  • 구문 재구성;

아무 것도 할 수없는 기능입니다.

지연된 평가

고려

(defmacro unless [test then] 
    (list 'if (list 'not test) then))) 

... if-not의 일부 클론 (프로그래밍 Clojure의에서 Halloway & Bedra 의해). nil를 반환 0으로 분열을 방지하는 ... 이제

(defn safe-div [num denom] 
    (unless (zero? denom) (/ num denom))) 

을 정의하는 데 사용하자 :

(safe-div 10 0) 
=> nil 

우리가 함수로 정의하려고하는 경우 :

(defn unless [test then] 
    (if (not test) then)) 

을 .. 그 다음

(safe-div 10 0) 
ArithmeticException Divide by zero ... 

unless의 본문이 무시되기 전에 잠재 결과는 unless으로의 then 인수로 평가됩니다.

캡처 양식 및 구문를 다시 조직은

한다고 가정 Clojure의 더 case 형태 없었다. 키 (ks) 및 해당 표현 (vs) 떨어져

(defmacro my-case [expr & stuff] 
    (let [thunk (fn [form] `(fn [] ~form)) 
     pairs (partition 2 stuff) 
     default (if (-> stuff count odd?) 
        (-> stuff last thunk) 
        '(constantly nil)) 
     [ks vs] (apply map list pairs) 
     the-map (zipmap ks (map thunk vs))] 
    (list (list the-map expr default)))) 

  • 픽,
  • 이 매개 변수가 fn 형태로
  • 을 후자를 래핑 : 여기에 졸속 대체입니다
  • 은 앞에서 후자로 맵을 구성합니다.
  • 은 검색하여 반환 된 함수를 호출하는 폼을 반환합니다. 지도.

세부 사항은 중요하지 않습니다. 요점은 그것이 완료 될 수 있다는 것입니다.

귀도 반 로섬 (Guido van Rossum)이 파이썬에 사례 발표문을 추가 할 것을 제안했을 때위원회는 그를 기각했다. 따라서 파이썬에는 어떠한 case 문도 없다. 리치가 case 성명을 원하지 않았지만 내가 한 경우, 그 중 하나를 가질 수 있습니다.


그냥 재미를 위해,의는 if 형태 무난 함 복제를 고안하기 위해 매크로를 사용할 수 있습니다. 이것은 기능 프로그래밍 분야의 진부함에 의심의 여지가 없지만 나를 놀래켜주었습니다. 나는 if을 게으른 평가의 기약적인 기본 요소로 생각했다.

(defmacro if-like 
    ([test then] `(if-like ~test ~then nil)) 
    ([test then else] 
    `(my-case ~test 
    false ~else 
    nil ~else 
    ~then))) 

이 장황한 느린이며, 스택을 사용하고 폐쇄에 묻혀됩니다 recur을 잃는다 :

쉬운 방법은 상기 my-case 매크로에 피기 백이다. 그러나 ...

(defn fact [n] 
    (if-like (pos? n) 
    (* (fact (dec n)) n) 
    1)) 

(map fact (range 10)) 
=> (1 1 2 6 24 120 720 5040 40320 362880) 

... 더 많거나 적습니다.


제발, 내 자신의 코드에서 오류가 발생했습니다.

+0

() 예제는 λ 함수로 unless 함수를 사용하여 λ 식으로 전달하여 수행 할 수도 있습니다. 심지어 람다가하지 않을 경우가 있습니까? –

+0

@TuomasToivonen 나는 람다가 항상 그것을 할 수 있다고 생각한다. 아시다시피, 함수가 연기 할 수없는 것은 그 인수에 대한 평가입니다. 틀림없이 람다 (lambdas)에서 포착 된 평가되지 않은 형태로서 항상 논증을 제공 할 수 있습니다. lambda를 사용하여 매크로처럼 'if'모양을 만들 수도 있습니다 : look, ma, 특별한 형식이 아닙니다. 사실, 지금 당장 할거야 :). – Thumbnail