racket — как использовать элементы списка в качестве процедуры (функции)

#function #scheme #racket #symbols #quote

#функция #схема #ракетка #символы #Цитата

Вопрос:

У меня есть список, который содержит «имена» некоторых функций (например '( - *) )

Я хочу использовать этот список для вызова функций

 (define list_of_names_of_functions '(  - *))

(define sum (car list_of_names_of_functions))

(sum 2 3)
 

Однако sum — это список, поэтому его нельзя использовать как процедуру.

Как я должен это сделать?

Ответ №1:

Переменная list_of_names_of_functions имеет символьное представление, а не фактическую функцию. возможно, вам следует создать список с именем и функцией следующим образом:

 (define ops `((  . , ) (- . ,-) (* . ,*)))
 

Если вы оцените ops , вы увидите что-то вроде:

 ((  . #<procedure: >) 
 (- . #<procedure:->) 
 (* . #<procedure:*>))
 

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

 ((cdar ops) 1 2) ; ==> 3
 

Таким образом, вы можете искать с помощью:

 (assq '  ops)
; ==> (  . #<procedure: >)
(assq '/ ops)
; ==> #f
 

Таким образом, если результат верен, он cdr будет содержать процедуру:

 (apply (cdr (assq '  ops)) '(1 2)) ; ==> 3
 

Если вы создаете специальный eval дочерний элемент, то вы бы рассматривали список как среду, и его можно легко добавить к тому, что вы можете добавить / . Вы также можете указать что-либо вызываемое, чтобы операция не обязательно существовала в хост-системе. например. `(square . ,(lambda (v) (* v v)) работает также.

Рассмотрите возможность использования соглашения об именовании Scheme (и Lisp). Например. вместо list_of_names_of_functions него следует назвать list-of-names-of-functions

Ответ №2:

Простое и немедленное решение — использовать (define list_of_functions ( list - *)) , тогда с (define sum (car list_of_functions)) ним будет работать.

(редактировать:) Это происходит потому list , что это функция, которая вычисляет свои аргументы и возвращает результаты в виде списка. Тогда quote как (или ' перед выражением) предотвращает вычисление его аргумента.

'(A B ...) эквивалентно (quote (A B ...)) ; это (A B ...) список символов, и цитата сохраняет его как таковой.

Это список символов, потому что именно так читается код Lisp либо из исходного файла, либо из REPL, то есть по подсказке интерпретатора. Другие языки имеют свой код в виде строк в файле исходного кода, а компилятор — это некая внешняя магия, которая живет и работает полностью вне языка. В языках, подобных Lisp, код — это данные. Код считывается из текстового файла исходного кода в виде (т.е. Строки превращаются в) вложенных списков символов, и явная eval , а также неявная оценка глубоко внутри самого языка.

Таким образом, то, что у вас было, эквивалентно списку

 (define list_of_names (list '  '- '*))
 

который содержит недооцененные символы, в отличие от списка

 (define list_of_functions (list   - *))
 

который содержит их значения, которые оказываются обычными встроенными функциями, обозначаемыми этими «именами».

И мы можем вызвать функцию, но мы не можем вызвать символ (во всяком случае, не в схеме).

Ответ №3:

Как я упоминал в комментарии, using eval — неправильный ответ на этот вопрос: он «работает», но открывает путь к ужасам.

Если у вас есть подобный символ , и вы хотите получить его динамическое значение, то подход ракетки к этому заключается в использовании пространств имен. Пространство имен — это действительно машина, которая превращает символы в значения тем же способом, что eval и делает, но это все, что она делает, в отличие eval . К сожалению, я не очень хорошо разбираюсь в пространствах имен, но это сработает:

 (define-namespace-anchor nsa)
(define ns (namespace-anchor->namespace nsa))

(define (module-symbol-value s (in ns))
  (namespace-variable-value s #t #f in))
 

Затем

 (define function-names '(  - / *))
(define sum (module-symbol-value (first function-names)))
 

И теперь

 > (sum 1 2 3)
6
> (eq? sum  )
#t
>
 

Проблема с этим подходом заключается в том, что он протекает: если у вас есть что-то вроде этого:

 (module msv racket
  (provide module-symbol-value)

  (define-namespace-anchor nsa)
  (define ns (namespace-anchor->namespace nsa))

  (define secret "secret")
  
  (define (module-symbol-value s (in ns))
    (namespace-variable-value s #t #f in)))

(require 'msv)

(define oops (module-symbol-value 'secret))
 

Теперь oops есть secret . Способ обойти это — использовать make-base-namespace для получения пространства имен, эквивалентного racket/base .

Ответ №4:

Я только что нашел ответ, извините.

Решением будет использование функции «eval»

 (define list_of_names_of_functions '(  - *))

(define sum (car list_of_names_of_functions))

((eval sum) 2 3)
 

Комментарии:

1. Использование eval похоже на использование ядерного оружия: это эффективный инструмент, но проблемы, связанные с его использованием, являются, ну, проблемами.