В Erlang, как вы вызываете функцию динамически?

Я хочу вызвать xyz с именем вызываемой функции.

-module(sample).
-export([xyz/1]).

xyz(Name) -> Name().

p() -> "you called p".
g() -> "you called g".

Но я получаю следующую ошибку:

1> c(sample.erl).
./sample.erl:6: Warning: function p/0 is unused
./sample.erl:7: Warning: function g/0 is unused
{ok,sample}
2> sample:xyz('p').
** exception error: bad function p
     in function  sample:xyz/1
3>

Ответы

Ответ 1

Правильно, что вам нужно экспортировать p и g. Затем вы можете использовать apply/3 для его вызова.

erlang:apply(sample, p, [])

Только забавные значения можно использовать с синтаксисом Fun (...). Вы передаете значение атома. Атом - это "плохая функция", так как сообщение об ошибке идет. Вы можете сделать что-то похожее на

xyz(p) -> fun p/0;
xyz(g) -> fun g/0.

Затем перейдите и вызовите

Fun = xyz(p),
Fun()

Ответ 2

-module(sample).
-export([xyz/1, p/0, g/0]).

xyz(Name) -> ?MODULE:Name().

p() -> "you called p".
g() -> "you called g".


1> sample:xyz(p).
"you called p"

Ответ 3

Сравнение шаблонов - это идиома, которую следует использовать:

-module(sample).
-export([xyz/1]).

xyz(p) -> p();
xyz(q) -> g().

p() -> "you called p".
g() -> "you called g".

Если вы хотите быть динамическим, вы можете использовать сервер gen_event.

По сути, это сервер, который содержит состояние, состоящее из пары ключ/функция, например:

[{p, #func1},
 {g, #func2},
 {..., ...},
 ...]

Затем вы можете существенно привязать события к функциям. (есть, разумеется, немного больше, чем это.

Ответ 4

Самый простой способ - попробовать экспортировать p и g вместе с xyz.

-export([xyz/1, p/0,g/0]).

После экспорта функции p и g можно вызвать следующим образом:

1> sample:xyz(fun sample:p/0).
"you called p"
2> sample:xyz(fun sample:g/0).
"you called g"

Ответ 5

Другим способом взглянуть на это является то, что (в зависимости от проблемы, которую вы решаете) динамические вызовы функций не всегда являются правильным подходом. Учитывая, что процессы и передача сообщений - это способ организации вашего кода в Erlang, поскольку это "ориентированный на concurrency язык", возможно, вы могли бы просто использовать передачу сообщений с выборочным приемом, а не имитировать идиомы последовательного языка? Отправьте сообщение, для чего вы хотите, и получите его на основе этого. Это ведь результат каждой функции, а не сама функция, в конце концов. (Кроме того, гибкость и масштабируемость передачи сообщений и т.д.)

Несмотря на то, что процессы не являются полностью бесплатными по сравнению с вызовом из библиотечного модуля, процессы уровня Erlang являются грязными (особенно, если связь сообщений находится в пределах одного и того же node). Они не являются процессами на уровне ОС. Накладные расходы были бы сопоставимы (или лучше) с динамическими вызовами функций и созданием объектов на более тяжелых языках сценариев.