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의 차이를 알고하지 않습니다 nil및 false.
— 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.