Вызов функции Erlang со значениями по умолчанию и/или без определенного порядка

Я новичок в Erlang и пытаюсь найти лучший способ получить значения по умолчанию для вызова функции, который требует нескольких переменных и/или также не хочет вводить аргументы в определенном порядке. В настоящее время я использую этот формат, основанный на Clojure. Есть ли лучший способ или метод для достижения этого в Erlang? Я также включил пример Clojure в качестве ссылки:

Эрланг версия:

some_function_with_defaults() ->
    some_function_with_defaults(#{}).

some_function_with_defaults(Map) ->
    Defaults = #{
                arg1 => 0, % arg1 default value
                arg2 => 1, % arg2 default value
                arg3 => 2  % arg3 default value
              },

  Arguments = maps:merge(Defaults,Map),

  #{arg1 := Arg1} = Arguments,
  #{arg2 := Arg2} = Arguments,
  #{arg3 := Arg3} = Arguments,

  %% Do something with arguments
  [Arg1,Arg2,Arg3].

%% Example call using only defaults
%% some_function_with_defaults().
%%
%% [0,1,2]

%% Example call specifying a specific value
%% some_function_with_defaults(#{arg1 => 99}).
%%
%% [99,1,2]

Ссылка на Clojure:

(defn some-function-with-defaults 
    [
     & {:keys
        [
         arg1
         arg2
         arg3

        ]
        :or
        {
         arg1 0 ;; arg1_default_value
         arg2 1 ;; arg2_default_value
         arg3 2 ;; arg3_default_value
        }
       }
  ]

  ;; Do something with arguments
  [arg1,arg2,arg3]
)

;; Example call using only defaults
;; (some-function-with-defaults)
;; 
;; [0,1,2]

;; Example call specifying a specific value
;; (some-function-with-defaults :arg1 99)
;;
;; [99,1,2]

person casillic    schedule 02.08.2015    source источник
comment
Erlang — это не Clojure, Python или что-то даже отдаленно близкое. Причина, по которой в Erlang нет встроенной возможности для этого, заключается в том, что в этом нет никакого смысла (буквально нет преимуществ, а скорее много недостатков). Если вы хотите писать на Clojure, пишите на Clojure. Не пытайтесь писать Clojure на Erlang, таким образом вы только затолкнете себя в невероятно странные места. Например, какая польза от Dialyzer сейчас? Что вы можете знать о форме ваших данных сейчас? и т. д.   -  person zxq9    schedule 03.08.2015
comment
С уважением, я не защищаю один язык над другим. Это простой справочный пример, который мог бы быть на любом языке. Но в этом конкретном случае мне нужно преобразовать проект кода из Clojure в Erlang. (реальный мир)... Я видел записи Erlang, используемые аналогичным образом (переданные в функции), где записи допускают значения по умолчанию, будет ли это лучшим подходом? Так как вы против этого метода. Опять же, я не пытаюсь продать кому-либо этот метод, но существующая кодовая база основана на нем. Каков наилучший способ обработки значений по умолчанию для функций в Erlang ?? Просто ищу совета.   -  person casillic    schedule 03.08.2015
comment
Запись значений по умолчанию — это один из подходов, еще лучше — непрозрачный тип, который вы абстрагируете где-то еще. С непрозрачным типом вам все равно, находятся ли данные на карте, в записи, в словаре или где-то еще — и с непрозрачным типом вы все равно можете предоставить гарантию типизации вместе со значениями по умолчанию или чем угодно. Ключевым моментом здесь является понимание того, что существует разница между предоставлением значений по умолчанию в структуре данных и аргументами по умолчанию. Это не одно и то же. Эрланг функционален, и чтобы правильно им пользоваться, вы должны принять семантику реальных функций (в отличие от процедур).   -  person zxq9    schedule 03.08.2015
comment
Другой подход состоит в том, чтобы передать пролист в функцию как единственный параметр.   -  person Mickaël Rémond    schedule 03.08.2015


Ответы (1)


Один из подходов, который я часто видел до того, как появились карты, заключается в следующем:

  • пролист с необязательными аргументами
  • запись со значениями по умолчанию
  • функция, которая берет значения из пропслиста и помещает их в запись.
-record(options, {
    opt_int = 42     :: integer(),      % default is 42
    opt_bool = false :: boolean(),      % default false
    opt_atom         :: undefined | val % default undefined
}).
parse_opts(Opts) ->
    parse_opts(Opts, #options{}).

parse_opts([], Res) ->
    Res;
parse_opts([{opt_int, Val} | RestOpts], Res) when is_integer(Val) ->
    parse_opts(RestOpts, Res#options{opt_int = Val});
parse_opts([{opt_bool, Val} | RestOpts], Res) when is_boolean(Val) ->
    parse_opts(RestOpts, Res#options{opt_bool = Val});
parse_opts([{opt_atom, Val} | RestOpts], Res) when Val == undefined; Val == val ->
    parse_opts(RestOpts, Res#options{opt_atom = Val}).

parse_opts([{opt_int, 55}]) даст вам #options{opt_int = 55, opt_bool = false, opt_atom = undefined}

Это немного неудобно, в основном потому, что записи — это синтаксический сахар времени компиляции, а не кортежи. Однако вы можете сделать более элегантную вещь с картами.

P.S. не проверял это, поэтому может содержать несколько ошибок.

П.П.С. Это вызовет исключение, если вы передадите неизвестный параметр.

person danechkin    schedule 10.08.2015
comment
+1: Proplists — это обычный способ сделать это, часто с функцией преобразования в запись, такой как здесь. - person Roger Lipscombe; 10.08.2015