LISP - Разделительная строка с разделителем также включена в новый список - PullRequest
0 голосов
/ 25 ноября 2018

У меня есть список элементов, следующих за

("(aviyon" "213" "flyingman" "no))") as list

Я хочу разделить этот список, содержащий строки, используя скобки в качестве разделителя, но также хочу включить эти скобки в новый список, не ломаяorder

Мой желаемый вывод нового списка (или того же самого измененного списка)

("(" "aviyon" "213" "flyingman" "no" ")" ")") 

Я исхожу из императивных языков, и это будет 15-минутная работа на Java или C ++.Но здесь я застрял, что делать.Я знаю, что должен

1 - получить элемент из списка в цикле

Я думаю, что это делается с (nth 1 '(listname) )

2 - отдельно без удаления разделителя, вставленного вв новый список

Я нашел такие функции, как SPLIT-SEQUENCE, но я не могу обойтись без его удаления и без нарушения первоначального порядка.

Любая помощь будет оценена.

Ответы [ 3 ]

0 голосов
/ 25 ноября 2018

решение

Поскольку вы не поняли решение Александра и поскольку я все равно написал свое решение:

;; load two essential libraries for any common lisper
(ql:quickload :cl-ppcre)
(ql:quickload :alexandria)
;; see below to see how to install quicklisp for `ql:quickload` command
;; it is kind of pythons `import` and if not install `pip install`
;; in one command for common-lisp

(defun remove-empty-string (string-list) 
  (remove-if #'(lambda (x) (string= x "")) string-list))


(defun split-parantheses-and-preserve-them (strings-list)
  (remove-empty-string 
  (alexandria:flatten 
    (mapcar #'(lambda (el) (cl-ppcre:split "(\\(|\\))" 
                                           el 
                                           :with-registers-p t)) 
            strings-list))))

 ;; so now your example
 (defparameter *list* '("(aviyon" "213" "flyingman" "no))"))

 (split-parantheses-and-preserve-them *list*)
 ;; returns:
 ;; ("(" "aviyon" "213" "flyingman" "no" ")" ")")

как это работает

(cl-ppcre:split "(\\(|\\))" a-string) разбивает строку на ( или ).Поскольку в шаблоне регулярных выражений ( или ) используются для захвата совпадения - как и здесь (внешние паразиты захватывают) - вы должны избегать их.\\( или \\).Таким образом, с помощью cl-ppcre:split вы можете разбить любую строку в общем lisp по регулярному выражению.Супер крутой и супер эффективный пакет, написанный Эди Вейц.Он написал несколько очень сложных пакетов для распространенного языка - их также называют ediware или edicls в сообществе.Кстати, cl-ppcre даже более эффективен и быстрее, чем золотой стандарт для регулярных выражений: опция perl regex engine!

:with-regiesters-p t затем сохраняет соответствующий разделитель - который должен быть заключен в такие круглые скобки, как этот: (<pattern>) в шаблоне.

mapcar это над списком, чтобы применить его к каждому строковому элементу в вашем списке строк.

Однако после этого вы получите списоксписки.(Каждый внутренний список, содержащий разделенный результат для каждого строкового элемента списка).

Свести список на alexandria:flatten.Для многих функций, которые не соответствуют стандарту lisp, но которые, как вы думаете, являются базовыми - например, выровнять список, - всегда смотрите сначала в Александрии - в основном это функция, которую вы хотите - это огромная библиотека.Вот почему он вам все равно нужен как обычный шепот;).

Но все равно вокруг будут пустые строки, которые нужно удалить.Вот почему я написал remove-empty-string, который использует remove-if - который вместе с remove-if-not является стандартной функцией фильтрации для списков.Требуется функция предиката - здесь (lambda (x) (string= x "")), которая дает T, если строка является пустой строкой, и NIL, если нет.Он удаляет все элементы в результирующем сглаженном списке в нашей функции, которые являются пустыми строками.В других языках это будет называться filter, но да - иногда имена функций в Common-LISP выбираются не очень хорошо.Иногда я думаю, что мы должны создать псевдонимы и перейти к ним и сохранить старые имена для обратной совместимости.У Clojure есть более приятные имена для функций ... Может быть, люди должны обгонять имена функций clojure ...

quicklisp

@ Александр Артеменко написал именно мое решение - он пришел первым,Я добавлю: если вы новичок в обычном lisp, возможно, вы не знаете, как использовать quicklisp.Делать в терминале (linux или macos):

wget https://beta.quicklisp.org/quicklisp.lisp

В противном случае вручную загружать в windows с адреса.

Я помещаю его в папку ~/quicklisp.

Затем вclisp или sbcl do:

(load "~/quicklisp/quicklisp.lisp") ;; just path to where downloaded
;; quicklisp.lisp file is!

;; then install quicklisp:
(quicklisp-quickstart:install)

;; then search for cl-ppcre
(ql:system-apropos "cl-ppcre")

;; then install cl-ppcre
(ql:quickload "cl-ppcre")

;; and to autoload everytime you start sbcl or clisp
;; in linux or mac - sorry I don't now windows that well
;; I have the opinion every programmer should us unix
;; as their OS
;; you have to let quicklisp be loaded when they start
;; by an entry into the init file
;; mostly located in ~/.sbclrc or ~/.clisprc.slip or such ...
;; respectively.
;; quicklisp does an entry automatically if you do:
(ql:add-to-init-file)

;; after installation do:
(quit)

;; If you then restart sbcl or clisp and try:
(ql:quickload :cl-ppcre)
;; it should work, - if not, you have to manually load
;; quicklisp first
(load "~/quicklisp/setup.lisp") ;; or wherever quicklisp's
;; setup.lisp file has been stored in your system!
;; and then you can do
(ql:quickload :cl-ppcre)

;; to install alexandria package then, do
(ql:quickload :alexandria) ;; or "alexandria"

;; ql:quickload installs the package from quicklisp repository,
;; if it cannot find package on your system.

;; learn more about quicklisp, since this is the package
;; manager of common lisp - like pip for python
0 голосов
/ 26 ноября 2018

Давайте ответим еще, без внешних библиотек.Как вы уже сделали, мы можем разбить задачу на более мелкие части:

  1. определить функцию, которая создает список токенов из строки, all-tokens
  2. применитьэта функция на всех строках в вашем входном списке и объединяет результат:

    (mapcan #'all-tokens strings)
    

Первая часть, берущая состояние и строящая из него список, выглядит как unfoldоперация (анаморфизм).

Fold (катаморфизм), называемый reduce в Лиспе, строит значение из списка значений и функцию (и, необязательно, начальное значение).Двойная операция, unfold, принимает значение (состояние), функцию и генерирует список значений.В случае unfold, функция шага принимает состояние и возвращает новое состояние вместе с результирующим списком.

Здесь давайте определим состояние как 3 значения: строка, начальная позиция в строке,и стопка токенов разобрана до сих пор.Наша пошаговая функция next-token возвращает следующее состояние.

 ;; definition follows below
 (declare (ftype function next-token))

Основная функция, которая получает все токены из строки, просто вычисляет точку фиксации:

(defun all-tokens (string)
  (do (;; initial start value is 0
       (start 0)
       ;; initial token stack is nil
       (tokens))

      ;; loop until start is nil, then return the reverse of tokens
      ((not start) (nreverse tokens))

    ;; advance state
    (multiple-value-setq (string start tokens)
      (next-token string start tokens))))

Нам нужна вспомогательная функция:

(defun parenthesisp (c)
  (find c "()"))

Шаговая функция определяется следующим образом:

(defun next-token (string start token-stack)
  (let ((search (position-if #'parenthesisp string :start start)))
    (typecase search
      (number
       ;; token from start to parenthesis
       (when (> search start)
         (push (subseq string start search) token-stack))
       ;; parenthesis
       (push (subseq string search (1+ search)) token-stack)
       ;; next state
       (values string (1+ search) token-stack))
      (null
       ;; token from start to end of string
       (when (< start (1- (length string)))
         (push (subseq string start) token-stack))
       ;; next-state
       (values string nil token-stack)))))

Вы можете попробовать с одной строкой:

(next-token "(aviyon" 0 nil)
"(aviyon"
1
("(")

Если вы берете значения результирующего состоянияи повторно использовать их, у вас есть:

(next-token "(aviyon" 1 '("("))
"(aviyon"
NIL
("aviyon" "(")

И здесь второе возвращаемое значение - NIL, которое завершает процесс генерации.Наконец, вы можете сделать:

(mapcan #'all-tokens '("(aviyon" "213" "flyingman" "no))"))

Что дает:

("(" "aviyon" "213" "flyingman" "no" ")" ")")

Приведенный выше код не является полностью универсальным в том смысле, что all-tokens слишком много знает о next-token: выможет переписать его, чтобы принять любое состояние.Вы также можете обрабатывать последовательности строк, используя тот же механизм, сохраняя больше информации в вашей переменной состояния.Кроме того, в реальном лексере вы бы не хотели переворачивать весь список токенов, вы бы использовали очередь для подачи синтаксического анализатора.

0 голосов
/ 25 ноября 2018

Вы можете использовать библиотеку cl-ppcre для выполнения этой работы.

Например:

CL-USER> (ql:quickload :cl-ppcre)

CL-USER> (cl-ppcre:split "([\\(\\)])" "(aviyon" :with-registers-p t)
("" "(" "aviyon")
CL-USER> (cl-ppcre:split "([\\(\\)])" "no))" :with-registers-p t)
("no" ")" "" ")")
CL-USER> 

Однако в списке отображаются пустые строки.Используйте функцию remove-if, чтобы избавиться от них:

CL-USER> (defun empty-string-p (s) (string= s ""))
EMPTY-STRING-P
CL-USER> (remove-if 'empty-string-p
                    (list "no" ")" "" ")"))
("no" ")" ")")

Наконец, вы можете создать функцию, которая выполняет обе функции, и запустить ее в цикле imperative (да, Common Lisp не так функционален, как многиедумаю):

CL-USER> (defun remove-empty-strings (l)
           (remove-if 'empty-string-p l))
REMOVE-EMPTY-STRINGS
CL-USER> (defun split (s)
           (cl-ppcre:split "([\\(\\)])"
                           s
                           :with-registers-p t))
SPLIT
CL-USER> (defparameter *the-list* '("(aviyon" "213" "flyingman" "no))"))
*THE-LIST*
CL-USER> (loop for item in *the-list*
               for splitted = (split item)
               for cleaned = (remove-empty-strings splitted)
               append cleaned)
("(" "aviyon" "213" "flyingman" "no" ")" ")")
...