Хорошее питоническое `объединение` в common-lisp - PullRequest
0 голосов
/ 01 июня 2018

В кулинарной книге Эди Вейц, для pythonic join, предлагается эта функция:

(defun join (separator list)
  (with-output-to-string (out)
    (loop for (element . more) on list
          do (princ element out)
          when more
            do (princ separator out))))

Однако, как-то я думал, что должен быть способ выразить join по-другому, возможно, используя возможности format ...

В книге Сейбеля (в главе о format) мы находим соединение строк в списке в одну строку с разделителем ", " с помощью:

(defvar l '("a" "b" "c"))

(format nil "~{~A~^, ~}" l)
;; "a, b, c"

Который является питоническим соединением и который очень краткий;директива ~^ означает, что ", " добавляется только до последнего элемента и не добавляется, когда ни один элемент не следует за ним.

Однако здесь разделительная строка ", " является частью директивы формата.

Хитрый случай, например (defvar sep #\Tab).Если представление sep "#\Tab" буквально можно поместить в качестве разделителя в середине этой директивы формата, в результате чего:

(format nil "~{~A~^#\Tab~}" l)

Мы бы достигли цели.

Очевидно, одиндолжен использовать макрос для генерации директивы формата ... Я пробовал что-то вроде (princ-to-string sep), но это дает "#\\Tab", а не "#\Tab".

Например

(defmacro join (sep l)
  `(format nil ,(format nil "~{~A~}" `("\~\{\~A\~\^" ,(write-to-string sep) "\~\}")) l))

Но когдапытаясь:

(join #\Tab '("a" "b" "c"))

Этот результат, конечно, не желателен: "a#\\Tabb#\\Tabc", так как

(macroexpand-1 '(join #\Tab '("a" "b" "c")))
;; results in:
(FORMAT NIL "~{~A~^#\\Tab~}" L)
;; instead of:
(FORMAT NIL "~{~A~^#\Tab~}" L)

Но я не вижу, как достичь этого шага для нужного макроса... У кого-нибудь есть понимание по этому поводу?

Вроде метапрограммирования по проблеме метапрограммирования ...

Хорошо, теперь я вижу, что @Rainer Joswig уже опубликовал в Что заканонический способ объединения строк в список?

решение этой проблемы.Однако, если бы был способ представить "#\\Tab" как "#\Tab", можно прийти к более компактному определению.Но почему-то читатель Lisp кажется в строке всегда распознающей "\Tab" как одну букву.Можно ли написать функцию для этого?

Примечание

В R специально для метапрограммирования существуют такие функции, как as.name("myvar"), которые генерируют символ myvar из строки "myvar".и выражения типа deparse(substitute(x)), который принимает символ x и создает из него буквальную строку "x".Deparse возвращает назад выполнение print() команд, в результате чего экранируются специальные символы.deparse(deparse(substitute(x))) будет, например, генерировать "\"x\"" - в то время как parse(text = ... ) вокруг этого выражения сделает из него "x" снова parse(text = deparse(deparse(substitute(x)))).Как такие вещи могут быть достигнуты в Common-Lisp?например, (a-special-function #\Tab) в результате (буквально): "#\Tab" в виде строки?

Эпилог

Спасибо @Sylwester !!Он решил это без макроса.И очень элегантно!

1 Ответ

0 голосов
/ 02 июня 2018

Ваша проблема в том, что вы пытаетесь добавить #\Tab к формату, но именно так вы делаете это с литералами.Если вы просто вставили символ табуляции или строку, состоящую из него, в строку формата, он будет делать то, что вы хотите:

(defun join (l &key (sep ", "))
  (format nil (format nil "~a~a~a" "~{~a~^" sep "~}") l))

(join '(1 2 3)) 
; ==> "1, 2, 3"
(join '(1 2 3) :sep #\Tab) 
; ==> "1    2   3"
...