Clojure에서 함수 인수에 대한 기본값을 작성하는 방법


127

나는 이것과 함께 온다 :

(문자열 정의-> 정수 [str & [base]]
  (정수 / 분석 구문 (if (nil? base) 10 base)))

(문자열-> 정수 "10")
(문자열-> 정수 "FF"16)

그러나이를 수행하는 더 좋은 방법이어야합니다.

답변:


170

서명이 서로 다른 경우 함수는 여러 서명을 가질 수 있습니다. 이를 사용하여 기본값을 제공 할 수 있습니다.

(defn string->integer 
  ([s] (string->integer s 10))
  ([s base] (Integer/parseInt s base)))

가정 false하고 nil둘 다 값이 아닌 것으로 간주 하면을 (if (nil? base) 10 base)단축하거나을 (if base base 10)더 단축 할 수 있습니다 (or base 10).


3
함수 이름을 반복하는 대신 (recur s 10)사용하여 두 번째 줄이 더 좋을 것이라고 생각합니다 . 그러면 향후 함수 이름을 쉽게 바꿀 수 있습니다. 이 상황에서 사용하지 않는 이유를 아는 사람 이 있습니까? recurstring->integerrecur
Rory O'Kane

10
그것은과 같은 recur동일한 인수에 대응에서만 작동합니다. 예를 들어, 위와 같이 되풀이를 시도한 경우java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
djhaskin987

이 같은 문제에 부딪쳤다. 함수 자체를 호출하는 것이 합리적 (string->integer s 10)입니까?
커트 뮬러

155

restClojure 1.2 [ ref ] 이후로 인수를 맵으로 재구성 할 수도 있습니다 . 이를 통해 함수 인수의 이름을 지정하고 기본값을 제공 할 수 있습니다.

(defn string->integer [s & {:keys [base] :or {base 10}}]
    (Integer/parseInt s base))

이제 전화 할 수 있습니다

(string->integer "11")
=> 11

또는

(string->integer "11" :base 8)
=> 9

https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (예 :) 에서이를 확인할 수 있습니다.


1
파이썬 배경에서 온다면 이해하기 쉽다 :)
Dan

1
이것은 받아 들여진 대답보다 이해하기가 훨씬 쉽다 ... 이것이 "클로 주 리안" 으로 받아 들여 지는가 ? 이 문서에 추가해보십시오.
Droogans

1
문제해결 하기 위해 비공식 스타일 가이드에 문제추가했습니다 .
Droogans 2013

1
이 답변은 수락 된 답변보다 "적절한"방법을 사용하여보다 효과적으로 캡처하지만 둘 다 잘 작동합니다. (물론, Lisp 언어의 큰 힘은 일반적으로 동일한 기본 작업을 수행하는 여러 가지 방법이 있다는 것입니다)
johnbakers

2
이것은 나에게 조금 어려워 보였고 잠시 동안 그것을 기억하는 데 어려움이 있었으므로 조금 덜 장황한 매크로를 만들었습니다 .
akbiggs

33

이 솔루션은 원래 솔루션정신에 더 가깝지만 조금 더 깨끗합니다.

(defn string->integer [str & [base]]
  (Integer/parseInt str (or base 10)))

유사한 패턴 편리한 사용을 할 수 있습니다 or와 함께let

(defn string->integer [str & [base]]
  (let [base (or base 10)]
    (Integer/parseInt str base)))

이 경우 더 자세한 정보를 제공하지만 다른 입력 값에 따라 기본값 을 설정하려는 경우 유용 할 수 있습니다 . 예를 들어 다음 기능을 고려하십시오.

(defn exemplar [a & [b c]]
  (let [b (or b 5)
        c (or c (* 7 b))]
    ;; or whatever yer actual code might be...
    (println a b c)))

(exemplar 3) => 3 5 35

이 접근법은 M. Gilliar의 솔루션에서 와 같이 명명 된 인수 로 작업하도록 쉽게 확장 할 수 있습니다 .

(defn exemplar [a & {:keys [b c]}]
  (let [b (or b 5)
        c (or c (* 7 b))]
    (println a b c)))

또는 더 많은 융합을 사용하십시오.

(defn exemplar [a & {:keys [b c] :or {b 5}}]
  (let [c (or c (* 7 b))]
    (println a b c)))

다른 기본값 (또는 가능하더라도)에 의존하는 기본값이 필요하지 않은 경우 위의 Matthew의 솔루션은 다른 변수에 대해 여러 기본값을 허용합니다. 그것은 정기적으로 사용하는 것보다 훨씬 청소기입니다or
johnbakers을

저는 Clojure 멍청한 놈이므로 OpenLearner가 옳을 수도 있지만 위의 Matthew의 솔루션에 대한 흥미로운 대안입니다. 궁극적으로 사용하기로 결정했는지 여부에 대해 알고 기쁩니다.
GlenPeterson

or다른 :or이후 or의 차이를 알고하지 않습니다 nilfalse.
Xiangru Lian

@XiangruLian : 또는 사용할 때 false를 전달하면 기본값 대신 false를 사용한다는 것을 알고 있습니까? 함께 또는 false 자체가 false가 아닌 경우 기본값을 사용합니까?
Didier A.

8

고려해야 할 또 다른 접근법이 있습니다 : 부분 함수. 이것들은 아마도 기능에 대한 기본값을 지정하는 더 "기능적"이고 더 유연한 방법입니다.

필요한 경우 기본 매개 변수로 제공하려는 매개 변수가있는 함수를 작성하여 (필요한 경우) 시작하십시오.

(defn string->integer [base str]
  (Integer/parseInt str base))

Clojure의 버전 partial에서는 함수 정의에 나타나는 순서대로 "기본"값을 제공 할 수 있기 때문에 수행 됩니다. 원하는대로 매개 변수를 주문한 후 함수를 사용하여 "기본"버전의 함수를 작성할 수 있습니다 partial.

(partial string->integer 10)

이 함수를 여러 번 호출 할 수있게하려면 다음을 사용하여 var에 넣을 수 있습니다 def.

(def decimal (partial string->integer 10))
(decimal "10")
;10

다음을 사용하여 "로컬 기본값"을 만들 수도 있습니다 let.

(let [hex (partial string->integer 16)]
  (* (hex "FF") (hex "AA")))
;43350

부분 함수 접근 방식은 다른 함수에 비해 한 가지 중요한 이점이 있습니다. 함수 소비자 는 함수 정의를 수정하지 않고도 함수 생성자 대신 기본값을 결정할 수 있습니다 . 이것은 기본 기능 이 내가 원하는 것이 아니라고 결정한 예제와 함께 설명됩니다 .hexdecimal

이 접근 방식의 또 다른 장점은 기본 기능에 더 설명적이고 다른 범위 (var, local) 일 수있는 다른 이름 (10 진수, 16 진수 등)을 지정할 수 있다는 것입니다. 원하는 경우 부분 기능을 위의 방법 중 일부와 혼합 할 수도 있습니다.

(defn string->integer 
  ([s] (string->integer s 10))
  ([base s] (Integer/parseInt s base)))

(def hex (partial string->integer 16))

(이 응답의 맨 위에 주어진 이유로 매개 변수의 순서가 바뀌 었으므로 이것은 Brian의 답변과 약간 다릅니다.)


1
이것은 질문이 요구하는 것이 아니다. 그래도 흥미 롭습니다.
Zaz

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.