Respostas:
Uma função pode ter várias assinaturas se as assinaturas diferirem em termos de área. Você pode usar isso para fornecer valores padrão.
(defn string->integer
([s] (string->integer s 10))
([s base] (Integer/parseInt s base)))
Observe que supor false
e nil
são considerados não-valores (if (nil? base) 10 base)
pode ser reduzido para (if base base 10)
ou além de (or base 10)
.
recur
só funciona na mesma aridade. se você tentou repetir acima, por exemplo:java.lang.IllegalArgumentException: Mismatched argument count to recur, expected: 1 args, got: 2, compiling:
(string->integer s 10)
)?
Você também pode desestruturar rest
argumentos como um mapa desde o Clojure 1.2 [ ref ]. Isso permite nomear e fornecer padrões para argumentos de função:
(defn string->integer [s & {:keys [base] :or {base 10}}]
(Integer/parseInt s base))
Agora você pode ligar
(string->integer "11")
=> 11
ou
(string->integer "11" :base 8)
=> 9
Você pode ver isso em ação aqui: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (por exemplo)
Esta solução está mais próxima do espírito da solução original , mas marginalmente mais limpa
(defn string->integer [str & [base]]
(Integer/parseInt str (or base 10)))
Um padrão semelhante que pode ser útil usa or
combinado comlet
(defn string->integer [str & [base]]
(let [base (or base 10)]
(Integer/parseInt str base)))
Embora neste caso seja mais detalhado, pode ser útil se você deseja ter padrões dependentes de outros valores de entrada . Por exemplo, considere a seguinte função:
(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
Essa abordagem pode ser facilmente estendida para trabalhar com argumentos nomeados (como na solução de M. Gilliar) também:
(defn exemplar [a & {:keys [b c]}]
(let [b (or b 5)
c (or c (* 7 b))]
(println a b c)))
Ou usando ainda mais uma fusão:
(defn exemplar [a & {:keys [b c] :or {b 5}}]
(let [c (or c (* 7 b))]
(println a b c)))
or
or
é diferente de :or
uma vez or
que não sabe a diferença de nil
e false
.
Há outra abordagem que você pode considerar: funções parciais . Essas são sem dúvida uma maneira mais "funcional" e mais flexível de especificar valores padrão para funções.
Comece criando (se necessário) uma função que tenha o (s) parâmetro (s) que você deseja fornecer como padrão (s) como o (s) parâmetro (s) principal (s):
(defn string->integer [base str]
(Integer/parseInt str base))
Isso é feito porque a versão do Clojure partial
permite fornecer os valores "padrão" somente na ordem em que aparecem na definição da função. Uma vez que os parâmetros foram ordenados conforme desejado, você pode criar uma versão "padrão" da função usando a partial
função:
(partial string->integer 10)
Para fazer com que essa função seja chamada várias vezes, você pode colocá-la em uma var usando def
:
(def decimal (partial string->integer 10))
(decimal "10")
;10
Você também pode criar um "padrão local" usando let
:
(let [hex (partial string->integer 16)]
(* (hex "FF") (hex "AA")))
;43350
A abordagem da função parcial tem uma vantagem importante sobre as outras: o consumidor da função ainda pode decidir qual será o valor padrão em vez do produtor da função, sem precisar modificar a definição da função . Isso é ilustrado no exemplo hex
em que decidi que a função padrão decimal
não é o que eu quero.
Outra vantagem dessa abordagem é que você pode atribuir à função padrão um nome diferente (decimal, hexadecimal etc.), que pode ser mais descritivo e / ou de escopo diferente (var, local). A função parcial também pode ser combinada com algumas das abordagens acima, se desejado:
(defn string->integer
([s] (string->integer s 10))
([base s] (Integer/parseInt s base)))
(def hex (partial string->integer 16))
(Observe que isso é um pouco diferente da resposta de Brian, pois a ordem dos parâmetros foi revertida pelas razões indicadas na parte superior desta resposta)
Você também pode acessar (fnil)
https://clojuredocs.org/clojure.core/fnil
(recur s 10)
, usando emrecur
vez de repetir o nome da funçãostring->integer
. Isso tornaria mais fácil renomear a função no futuro. Alguém sabe alguma razão para não usarrecur
nessas situações?