Следующий минимальный пример основан на этом блоке кода:
(define (nested_var_arg . (arg_1 . args))
(if (list? args)
(begin
(displayln "args")
(displayln args))
(if (arg_1)
(displayln arg_1)
(displayln "nothing"))))
Кажется, мы можем использовать 0 arg, как это делает (define (var_arg . lst) ... )
. Но (nested_var_arg)
выдаст ошибку «;Процедура #[communde-procedure 12nested_var_arg] была вызвана с 0 аргументами; для нее требуется как минимум 1 аргумент.».
R7RS 4.1.4 Процедуры дают пример без вложенного_var_arged (x y . z)
и «n или более аргументов», по-видимому, подразумевают, что и var_arg
, и nested_var_arg
принимают 0 аргументов.
Тогда как интерпретировать вышесказанное (nested_var_arg . (arg_1 . args))
?
В языках Lispy по соглашению используется kebab-case, а не Snake_case. nested-var-arg
, arg-1
и т. д.
@Шон 1. Есть ли хоть одна ссылка на «более традиционно написанное (nested_var_arg arg_1 . args)
»? 2. «В языках Lispy по соглашению используется kebab-case, а не Snake_case». Вероятно, это правда, поскольку SICP и SDF обычно его используют. Я использую «snake_case», так как в VSCode удобно выделять слово двойным кликом при использовании Snake_case вместо kebab-case.
Это фундаментальный аспект того, как средство записи отображает пары и как читатель интерпретирует литералы списка. См., например, раздел 6.4 R7RS.
@Шон Спасибо. Вы имеете в виду, что (arg_1 . args)
можно рассматривать как пару? Это нормально, поскольку args
может быть одним списком в (arg_1 . args)
. В standards.scheme.org/corrected-r7rs/… «(a b c . d) эквивалентно (a . (b . (c . d)))» подразумевает, что (c . d)
можно рассматривать как (. (c . d))
, хотя кажется невозможным составить вот такую, казалось бы, странную пару (. (c . d))
. Демонстрация: '(. (1 . 2))
выдаст ошибку «;Плохо сформированный пунктирный список: (.» и (cons '. (cons 1 2))
выдаст ошибку «;Точка разрешена только в списке».
(. (c . d))
— это синтаксическая ошибка, поскольку в (. whatever)
нет значения автомобиля, чтобы сделать его парой.
@ Шон Файн. Возможно, в Scheme есть какие-то специальные манипуляции с . (arg_1 . args)
в (nested-var-arg . (arg_1 . args))
, чтобы сделать его таким же, как (arg_1 . args)
, хотя я не знаю подробностей этого.
Возможно, это технически недопустимый синтаксис Scheme, поскольку синтаксис программ, я считаю, не определен в терминах «синтаксиса данных», который, например, понимается, а в терминах чего-то вроде BNF.
Таким образом, синтаксис процедуры в R7RS — это read
, а (lambda <formals> <body>)
— это <formals>
, <variable>
или (<variable> ...)
.
Но синтаксис данных для Scheme включает обычную нотацию Lisp для пар (conses) и эквивалентное сокращение для списков: пара — это (<variable> ... . <variable>)
, и вы всегда можете написать (x . y)
как (x . ())
и (x)
как (x . (y ...))
.
Таким образом, любая схема, которая использует обычный читатель для чтения программ, а также данных (а это, вероятно, все они), будет читать форму типа (x y . (...))
как полностью эквивалентную (define (nested_var_arg . (arg_1 . args)) ...)
.
Юрист по языку Scheme может ответить, законно ли это с технической точки зрения. Я попробовал три реализации, и все они в порядке.
Спасибо за ваше объяснение, основанное на программах чтения для чтения. Это подразумевается в первом комментарии Шона, но я не заметил этого, пока не прочитал ваш ответ. Итак, здесь нам следует рассматривать (nested_var_arg . (arg_1 . args))
как одну целую сущность, а не думать отдельно о . (arg_1 . args)
как о списке аргументов nested_var_arg
.
(nested_var_arg . (arg_1 . args))
?У пары есть машина и cdr. Список (x y)
представляет собой пару со списком (y)
в cdr (что вы можете проверить, вычислив (cdr '(x y))
. Это то же самое, что (x . (y))
, что то же самое, что и (x . (y . ()))
. Обычно вы пишете (x y)
вместо любой другой формы, представляющей это список, а (x y)
— это то, как принтер Scheme будет представлять любую из этих форм, но в каждом случае эти формы представляют один и тот же объект: список, содержащий x
и y
. Любая из этих форм является внешним представлением списка, содержащего x
и y
. и программа чтения схем преобразует внешние представления в объекты схемы. Это обсуждается в R7RS 3.3 Внешние представления , R7RS 7.1.2 Внешние представления и R7RS 6.13.2 Ввод.
Форма (nested_var_arg . (arg_1 . args))
— неправильный список или пунктирный список. Автомобиль nested_var_arg
и CDR (arg_1 . args)
. Это можно эквивалентно выразить как (nested_var_arg arg_1 . args)
; это внешние представления одного и того же несобственного списка. Внешние представления правильных и неправильных списков обсуждаются в R7RS 6.4 Пары и списки.
Определение может принимать одну из трех форм:
(define <variable> <expression>)
(define (<variable> <formals>) <body>)
, или(define (<variable> . <formal>) <body>)
.В третьем из них <formal>
обозначает одну переменную, поэтому (define (nested_var_arg arg_1 . args) ;...)
соответствует второму из них. Это описано в R7RS 5.3 Определения переменных. Здесь <formals>
соответствует формальному лямбда-выражению (lambda (arg_1 . args) ;...)
.
Формалы лямбда-выражения могут принимать одну из трех форм:
(<variable1> ...)
<variable>
, или(<variable1> ... <variablen> . <variablen+1>)
.Поскольку (arg_1 . args)
соответствует третьему из них, давайте сосредоточимся на этом. Тройная точка указывает, что для этой формы требуется хотя бы одна переменная (<variablen>
), за которой следует еще одна переменная (<variablen+1>
). Это обозначение описано в R7RS 1.3.3 Формат ввода. Полученная функция связывает аргументы, предшествующие точке, по отдельности и оборачивает все оставшиеся аргументы в список, привязанный к <variablen+1>
. Это означает, что функция, определенная с использованием формы (define (nested_var_arg . (arg_1 . args)) ;...)
, требует хотя бы одного аргумента, поскольку эта форма представляет собой неправильный список (define (nested_var_arg arg_1 . args) ;...)
. Вызов результирующей функции без аргументов вызывает ошибку, поскольку она была определена как требующая хотя бы одного аргумента, т. е. эта функция предполагает привязать аргумент к arg_1
и обернет все оставшиеся (необязательные) аргументы в список, привязанный к args
.
(define (var_arg . lst) ... )
.Это другая форма определения, которая соответствует третьей форме выше: (define (<variable> . <formal>) <body>)
. Здесь <formal>
соответствует формальному лямбда-выражению (lambda <formal> <body>)
(форма 2 выше, где <formal>
соответствует <variable>
). В результате этой формы получается функция, которая принимает любое количество аргументов и оборачивает их в список, привязанный к <variable>
. Вызов такой функции без аргументов допустим, и в результате к <variable>
будет привязан пустой список.
(define (nested_var_arg . (arg_1 . args)) ;...)
строго законной схемой?Схема требует, чтобы процедура read
анализировала форму (define (nested_var_arg . (arg_1 . args)) ;...)
, эквивалентную (define (nested_var_arg arg_1 . args) ;...)
, но только эта вторая форма является строго допустимым синтаксисом для define
. Схема позволяет реализации использовать read
для анализа программ, но не требует этого. Насколько я понимаю ситуацию, реализациям разрешено принимать форму OP define
и, скорее всего, так и будет, но реализация имеет право отклонить ее.
Я проголосовал за ваш ответ. Ваш ответ лучше, чем ответ ignis volens, если предположить меньший уровень знаний (я прочитал только главу 2 SICP и главу 2 SDF, чтобы изучить программирование). Поэтому я изменился, чтобы принять ваше. 1. 3 ссылки-ссылки в 1-м пункте очень помогают, особенно "скорее это внешнее представление трехэлементного списка, элементами которого являются символ + и целые числа 2 и 6". в 3.3. Это подразумевается в третьем комментарии Шона, но я не обратил на них внимания и сосредоточился только на «разделе 6.4 R7RS».
2. Спасибо за очень подробное объяснение, особенно за указание на «неправильный список». 3. Спасибо, что указали на мою ошибку, подразумеваемую в моем ручном соотношении (var_arg . lst)
и (nested_var_arg . (arg_1 . args))
и в вашем ответе «(<variable> . <formal>)
». 4. <variable1> ...
кажется особым случаем, поскольку в 1.3.3 говорится «указывает на ноль или более вхождений <вещи>», а в 4.1.4 standards.scheme.org/corrected-r7rs/… говорится «(это ошибка, если нет хотя бы одной)». Это также подразумевается в четвертом комментарии Шона.
Возможно, одна опечатка: (<variable1> . ... <variablen> <variablen+1>)
-> (<variable1> ... <variablen> . <variablen+1>)
.
Возможно, одна опечатка..." -- спасибо: исправлено. «кажется, это особый случай...» — мое объяснение здесь было туманным и немного небрежным; Я сделал некоторые уточнения. <variable1> ...
действительно указывает на ноль или более переменных, но <variable1> ... <variablen>
указывает на ноль или более переменных, за которыми следует одна обязательная переменная (<variablen>
).
Подсказка:
(nested_var_arg . (arg_1 . args))
пишется более традиционно(nested_var_arg arg_1 . args)