Предположим, у меня есть коллекция внешних структур данных в текстовом файле. Всякий раз, когда мне нужны данные для вычислений, мне приходится анализировать файл, а затем использовать его значения. Как сделать это удобнее?
write. Тогда полный список можно просто прочитать, нажав read. Конечно, но... (каждый раз нужно будет читать сложное представление данных в виде списка и впоследствии формировать все записи - этого можно было бы избежать, если бы данные уже были скомпилированы)Упрощенный пример:
Пусть комплексные данные представляют собой одно число во внешнем файле DATA.txt:
10
Давайте создадим модуль data.scm, который считывает внешние данные, сохраняет их и экспортирует:
(define-module (data))
(export DATA)
(define DATA (with-input-from-file "DATA.txt" read))
Давайте проверим этот подход с помощью test.scm:
(add-to-load-path (dirname (current-filename)))
(use-modules (data))
(format #t "DATA: ~a\n" DATA)
ЭТО, ПОКАЗЫВАЕТСЯ, РАБОТАЕТ (автокомпиляция включена или даже используется guild compile data.scm):
$ guile test.scm
DATA: 10
Однако, когда я удаляю файл DATA.txt, возникает ошибка, поскольку в (скомпилированном) модуле данных отсутствует файл DATA.txt для чтения!
Таким образом, проблема остается: как на самом деле хранить внешние данные в модуле во время компиляции?
ЧТО РАБОТАЕТ, НО УЖАСНО!
Чтобы сгенерировать файл модуля буквально, например:
(define DATA (with-input-from-file "DATA.txt" read))
(with-output-to-file "data.scm"
(lambda ()
(format #t
"(define-module (data))
(export DATA)
(define DATA ~a)\n"
DATA)))
Этот подход подобен использованию макросов C без использования преимуществ макросов схемы (т.е. синтаксиса). Неужели не существует элегантной схемы?
Спасибо, я понимаю вашу точку зрения. Мой вопрос, возможно, был нечетко сформулирован. Теперь я также привел пример в надежде показать, в чем проблема.
Вставляйте данные непосредственно в источник, а не загружайте его. Или, может быть, что-то с макросом, который читает его и генерирует переменную, может сработать.
Помещение данных непосредственно в модуль — это именно то, чего я хотел избежать. Макрос - это способ (см. мой ответ ниже), хотя все еще есть некоторые проблемы с синтаксисом.





Используя макросы Lispy, файл модуля data.scm можно закодировать следующим образом:
(define-module (data))
(export DATA)
(define-macro (define-get name file)
`(define ,name
,(with-input-from-file file read)))
(define-get DATA "DATA.txt")
Это своего рода решение. До сих пор не могу правильно написать с учетом синтаксиса, что сделало бы меня счастливее.
И до сих пор не понимаю, почему оригинал (define DATA ...) не работал, поскольку я всегда думал, что в схеме аргументы сначала оцениваются, прежде чем использоваться.
Вот пример того, как это сделать с помощью макроса syntax-case:
$ cat foo.scm
(define-module (foo)
#:export (data))
(use-modules (ice-9 textual-ports))
(define-syntax include-file
(lambda (stx)
(syntax-case stx ()
((_ name)
(let ((file-contents
(call-with-input-file (syntax->datum #'name) get-string-all)))
(datum->syntax #f file-contents))))))
(define data (include-file "./data.txt"))
$ cat data.txt
blah
$ sudo cp foo.scm /usr/share/guile/site/3.0/foo.scm
$ sudo guild compile -o /usr/lib64/guile/3.0/ccache/foo.go foo.scm
$ cd ..
$ guile
GNU Guile 3.0.9
Copyright (C) 1995-2023 Free Software Foundation, Inc.
Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.
Enter `,help' for help.
scheme@(guile-user)> (use-modules (foo))
scheme@(guile-user)> data
$1 = "blah\n"
Как вы можете видеть, после копирования исходного файла куда-то в пути загрузки Guile и предварительной компиляции его в байт-код вы можете загрузить модуль из других мест (или удалить файл данных), и данные по-прежнему доступны. Но на самом деле проще просто хранить данные непосредственно в файле схемы (возможно, с помощью программы, которая создает такой файл из необработанных данных, который можно запускать при обновлении данных), а использование автоматической компиляции Guile - это намного проще и менее трудоемко.
Ооо, оказывается, у хитрости есть оценка-когда. Возможно, здесь будет другой подход...
При первом импорте модуля Guile обычно компилирует его и кэширует скомпилированную версию для повторного использования. Или вы можете скомпилировать его заранее с помощью guild compile. Поэтому я бы рассмотрел ваш второй вариант (поместив ваши данные в отдельный модуль и импортировав их там, где это необходимо)