将通用序列系统与 jonathan 的列表一起使用 lisp

using Generic sequences system in common lisp with an alist from jonathan

我正在使用 messenger api from facebook, using ningle. there is a moment in my program that I need to work with this alist coming from jonathan:

CL-USER> (defparameter *params*
           '(("entry"
              (("messaging"
                (("message" ("text" . "hola") ("seq" . 3227)
                  ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
                 ("timestamp" . 1496071517968)
                 ("recipient" ("id" . "474086316315717"))
                 ("sender" ("id" . "1695095647186162"))))
               ("time" . 1496071518212) ("id" . "474086316315717")))
             ("object" . "page")))

*PARAMS*
CL-USER> (length *params*)
2

然后我必须处理入口部分:

CL-USER> (defparameter entries (cdr (assoc "entry" *params* :test #'string=)))
ENTRIES
CL-USER> entries
((("messaging"
   (("message" ("text" . "hola") ("seq" . 3227)
     ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
    ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
    ("sender" ("id" . "1695095647186162"))))
  ("time" . 1496071518212) ("id" . "474086316315717")))
CL-USER> (length entries)
1

然后我定义了两个函数来处理这个:

(defun extract-entry (entry)
  (let ((id (cdr (assoc "id" entry :test #'string=)))
        (time (cdr (assoc "time" entry :test #'string=)))
        (messaging (cdr (assoc "messaging" entry :test #'string=))))
    messaging))


(defun extract-messaging (event)
  (let ((message (cdr (assoc "message" event :test #'string=))))
    message))

那我操作:

CL-USER> (defparameter messaging (extract-entry (first entries)))
MESSAGING
CL-USER> messaging
((("message" ("text" . "hola") ("seq" . 3227)
   ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
  ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
  ("sender" ("id" . "1695095647186162"))))
CL-USER> (length messaging)
1


CL-USER> (defparameter message (extract-messaging (first messaging)))
MESSAGE
CL-USER> message
(("text" . "hola") ("seq" . 3227)
 ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
CL-USER> (length message)
3

而且效果很好,那么我想将 generic-sequences 应用到这部分:

CL-USER> (gen-seq:seq->list (gen-seq:seq-map #'extract-messaging messaging))
((("text" . "hola") ("seq" . 3227)
  ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u")))

CL-USER> (gen-seq:seq->list (gen-seq:seq-map #'extract-entry entries))
(((("message" ("text" . "hola") ("seq" . 3227)
    ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
   ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
   ("sender" ("id" . "1695095647186162")))))

它可以工作,但是当我连接函数时我遇到了这个问题:

CL-USER> (trace extract-entry extract-messaging)
(EXTRACT-ENTRY EXTRACT-MESSAGING)


CL-USER> (gen-seq:seq->list (gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries)))
  0: (EXTRACT-ENTRY
      (("messaging"
        (("message" ("text" . "hola") ("seq" . 3227)
          ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
         ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
         ("sender" ("id" . "1695095647186162"))))
       ("time" . 1496071518212) ("id" . "474086316315717")))
  0: EXTRACT-ENTRY returned
       ((("message" ("text" . "hola") ("seq" . 3227)
          ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
         ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
         ("sender" ("id" . "1695095647186162"))))
  0: (EXTRACT-MESSAGING
      ((("message" ("text" . "hola") ("seq" . 3227)
         ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
        ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
        ("sender" ("id" . "1695095647186162")))))
; Evaluation aborted on #<TYPE-ERROR expected-type:
             "(OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING SYMBOL CHARACTER)"
             datum: ("message" ("text" . "hola") ("seq" . 3227) ..)>.

在调试器中使用此输出:

The value
  ("message" ("text" . "hola") ("seq" . 3227)
   ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))

is not of type
  (OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING SYMBOL
      CHARACTER)

when binding SB-IMPL::STRING2
   [Condition of type TYPE-ERROR]

Restarts:
 0: [*ABORT] Return to SLIME's top level.
 1: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1003180003}>)

Backtrace:
  0: (STRING= "message" ("message" ("text" . "hola") ("seq" . 3227) ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))) [more]
  1: (SB-KERNEL:%ASSOC-TEST "message" #<unavailable argument> #<FUNCTION STRING=>)
  2: (EXTRACT-MESSAGING ((("message" # # #) ("timestamp" . 1496071517968) ("recipient" #) ("sender" #))))
  3: (SB-DEBUG::TRACE-CALL #<SB-DEBUG::TRACE-INFO EXTRACT-MESSAGING> #<FUNCTION EXTRACT-MESSAGING> ((("message" # # #) ("timestamp" . 1496071517968) ("recipient" #) ("sender" #))))
  4: ((LABELS GENERIC-SEQ::TRAVERSE :IN GENERIC-SEQ::SEQ-MAP-1) (((# # # #)) . #<CLOSURE (LAMBDA NIL :IN GENERIC-SEQ::SEQ-MAP-1) {1004CF1F5B}>))
  5: (GENERIC-SEQ:SEQ->LIST #S(GENERIC-SEQ::BASIC-SEQ :DELAYED-ENUM #<CLOSURE (LAMBDA NIL :IN GENERIC-SEQ::SEQ-MAP-1) {1004C9A46B}>))
  6: (SB-INT:SIMPLE-EVAL-IN-LEXENV (GENERIC-SEQ:SEQ->LIST (GENERIC-SEQ:SEQ-MAP (FUNCTION EXTRACT-MESSAGING) (GENERIC-SEQ:SEQ-MAP # ENTRIES))) #<NULL-LEXENV>)
  7: (EVAL (GENERIC-SEQ:SEQ->LIST (GENERIC-SEQ:SEQ-MAP (FUNCTION EXTRACT-MESSAGING) (GENERIC-SEQ:SEQ-MAP # ENTRIES))))
  8: (SWANK::%EVAL-REGION "(gen-seq:seq->list (gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries))) ..)
  9: ((LAMBDA NIL :IN SWANK::%LISTENER-EVAL))
 10: (SWANK-REPL::TRACK-PACKAGE #<CLOSURE (LAMBDA NIL :IN SWANK::%LISTENER-EVAL) {1004C9A1CB}>)
 11: (SWANK::CALL-WITH-BUFFER-SYNTAX NIL #<CLOSURE (LAMBDA NIL :IN SWANK::%LISTENER-EVAL) {1004C9A1AB}>)
 12: (SWANK::%LISTENER-EVAL "(gen-seq:seq->list (gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries))) ..)
 13: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-REPL:LISTENER-EVAL "(gen-seq:seq->list (gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries))) ..)
 14: (EVAL (SWANK-REPL:LISTENER-EVAL "(gen-seq:seq->list (gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries))) ..)
 15: (SWANK:EVAL-FOR-EMACS (SWANK-REPL:LISTENER-EVAL "(gen-seq:seq->list (gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries))) ..)
 16: (SWANK::PROCESS-REQUESTS NIL)
 17: ((LAMBDA NIL :IN SWANK::HANDLE-REQUESTS))
 18: ((LAMBDA NIL :IN SWANK::HANDLE-REQUESTS))
 19: (SWANK/SBCL::CALL-WITH-BREAK-HOOK #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL :IN SWANK::HANDLE-REQUESTS) {100317FFEB}>)
 20: ((FLET SWANK/BACKEND:CALL-WITH-DEBUGGER-HOOK :IN "/Users/toni/.roswell/lisp/slime/2017.02.27/swank/sbcl.lisp") #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL :IN SWANK::HANDLE-REQUESTS) {..
 21: (SWANK::CALL-WITH-BINDINGS ((*STANDARD-INPUT* . #1=#<SWANK/GRAY::SLIME-INPUT-STREAM {100309EAE3}>) (*STANDARD-OUTPUT* . #2=#<SWANK/GRAY::SLIME-OUTPUT-STREAM {1003167973}>) (*TRACE-OUTPUT* . #2#) (*ERR..
 22: (SWANK::HANDLE-REQUESTS #<SWANK::MULTITHREADED-CONNECTION {10030004A3}> NIL)
 23: ((FLET #:WITHOUT-INTERRUPTS-BODY-1159 :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE))
 24: ((FLET SB-THREAD::WITH-MUTEX-THUNK :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE))
 25: ((FLET #:WITHOUT-INTERRUPTS-BODY-358 :IN SB-THREAD::CALL-WITH-MUTEX))
 26: (SB-THREAD::CALL-WITH-MUTEX #<CLOSURE (FLET SB-THREAD::WITH-MUTEX-THUNK :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE) {8A4DCFB}> #<SB-THREAD:MUTEX "thread result lock" owner: #<SB-THREAD:THREAD "..
 27: (SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE #<SB-THREAD:THREAD "repl-thread" RUNNING {1003180003}> NIL #<CLOSURE (LAMBDA NIL :IN SWANK-REPL::SPAWN-REPL-THREAD) {100317FF6B}> (#<SB-THREAD:THREAD "re..
 28: ("foreign function: call_into_lisp")
 29: ("foreign function: new_thread_trampoline")
 30: ("foreign function: _pthread_body")
 31: ("foreign function: _pthread_body")
 32: ("foreign function: thread_start")

此时的问题是 extract-entry returns 一个列表所以我真的应该这样工作:

CL-USER> (apply #'mapcar #'extract-messaging (mapcar #'extract-entry entries))
  0: (EXTRACT-ENTRY
      (("messaging"
        (("message" ("text" . "hola") ("seq" . 3227)
          ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
         ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
         ("sender" ("id" . "1695095647186162"))))
       ("time" . 1496071518212) ("id" . "474086316315717")))
  0: EXTRACT-ENTRY returned
       ((("message" ("text" . "hola") ("seq" . 3227)
          ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
         ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
         ("sender" ("id" . "1695095647186162"))))
  0: (EXTRACT-MESSAGING
      (("message" ("text" . "hola") ("seq" . 3227)
        ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
       ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
       ("sender" ("id" . "1695095647186162"))))
  0: EXTRACT-MESSAGING returned
       (("text" . "hola") ("seq" . 3227)
        ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
((("text" . "hola") ("seq" . 3227)
  ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u")))

但随后无法使用库:

CL-USER> (apply #'gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries))

attempt to use VALUES-LIST on a dotted list:
  #S(GENERIC-SEQ::BASIC-SEQ
     :DELAYED-ENUM #<CLOSURE (LAMBDA ()
                               :IN
                               GENERIC-SEQ::SEQ-MAP-1) {100499978B}>)
   [Condition of type SIMPLE-TYPE-ERROR]

Restarts:
 0: [*ABORT] Return to SLIME's top level.
 1: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1003160003}>)

Backtrace:
  0: (APPLY #<FUNCTION GENERIC-SEQ:SEQ-MAP> #<CLOSURE SB-IMPL::ENCAPSULATION {1002E5970B}> #S(GENERIC-SEQ::BASIC-SEQ :DELAYED-ENUM #<CLOSURE (LAMBDA NIL :IN GENERIC-SEQ::SEQ-MAP-1) {100499978B}>))
  1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (APPLY (FUNCTION GENERIC-SEQ:SEQ-MAP) (FUNCTION EXTRACT-MESSAGING) (GENERIC-SEQ:SEQ-MAP (FUNCTION EXTRACT-ENTRY) ENTRIES)) #<NULL-LEXENV>)
  2: (EVAL (APPLY (FUNCTION GENERIC-SEQ:SEQ-MAP) (FUNCTION EXTRACT-MESSAGING) (GENERIC-SEQ:SEQ-MAP (FUNCTION EXTRACT-ENTRY) ENTRIES)))
  3: (SWANK::%EVAL-REGION "(apply #'gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries)) ..)
  4: ((LAMBDA NIL :IN SWANK::%LISTENER-EVAL))
  5: (SWANK-REPL::TRACK-PACKAGE #<CLOSURE (LAMBDA NIL :IN SWANK::%LISTENER-EVAL) {100499951B}>)
  6: (SWANK::CALL-WITH-BUFFER-SYNTAX NIL #<CLOSURE (LAMBDA NIL :IN SWANK::%LISTENER-EVAL) {10049994FB}>)
  7: (SWANK::%LISTENER-EVAL "(apply #'gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries)) ..)
  8: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SWANK-REPL:LISTENER-EVAL "(apply #'gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries)) ..)
  9: (EVAL (SWANK-REPL:LISTENER-EVAL "(apply #'gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries)) ..)
 10: (SWANK:EVAL-FOR-EMACS (SWANK-REPL:LISTENER-EVAL "(apply #'gen-seq:seq-map #'extract-messaging (gen-seq:seq-map #'extract-entry entries)) ..)
 11: (SWANK::PROCESS-REQUESTS NIL)
 12: ((LAMBDA NIL :IN SWANK::HANDLE-REQUESTS))
 13: ((LAMBDA NIL :IN SWANK::HANDLE-REQUESTS))
 14: (SWANK/SBCL::CALL-WITH-BREAK-HOOK #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL :IN SWANK::HANDLE-REQUESTS) {100315FFEB}>)
 15: ((FLET SWANK/BACKEND:CALL-WITH-DEBUGGER-HOOK :IN "/Users/toni/.roswell/lisp/slime/2017.02.27/swank/sbcl.lisp") #<FUNCTION SWANK:SWANK-DEBUGGER-HOOK> #<CLOSURE (LAMBDA NIL :IN SWANK::HANDLE-REQUESTS) {..
 16: (SWANK::CALL-WITH-BINDINGS ((*STANDARD-INPUT* . #1=#<SWANK/GRAY::SLIME-INPUT-STREAM {100307F5A3}>) (*STANDARD-OUTPUT* . #2=#<SWANK/GRAY::SLIME-OUTPUT-STREAM {1003147DD3}>) (*TRACE-OUTPUT* . #2#) (*ERR..
 17: (SWANK::HANDLE-REQUESTS #<SWANK::MULTITHREADED-CONNECTION {10030004A3}> NIL)
 18: ((FLET #:WITHOUT-INTERRUPTS-BODY-1159 :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE))
 19: ((FLET SB-THREAD::WITH-MUTEX-THUNK :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE))
 20: ((FLET #:WITHOUT-INTERRUPTS-BODY-358 :IN SB-THREAD::CALL-WITH-MUTEX))
 21: (SB-THREAD::CALL-WITH-MUTEX #<CLOSURE (FLET SB-THREAD::WITH-MUTEX-THUNK :IN SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE) {8A57CFB}> #<SB-THREAD:MUTEX "thread result lock" owner: #<SB-THREAD:THREAD "..
 22: (SB-THREAD::INITIAL-THREAD-FUNCTION-TRAMPOLINE #<SB-THREAD:THREAD "repl-thread" RUNNING {1003160003}> NIL #<CLOSURE (LAMBDA NIL :IN SWANK-REPL::SPAWN-REPL-THREAD) {100315FF6B}> (#<SB-THREAD:THREAD "re..
 23: ("foreign function: call_into_lisp")
 24: ("foreign function: new_thread_trampoline")
 25: ("foreign function: _pthread_body")
 26: ("foreign function: _pthread_body")
 27: ("foreign function: thread_start")

问题是一样的,但我可以解决它:

CL-USER> (gen-seq:seq->list (apply #'gen-seq:seq-map #'extract-messaging (gen-seq:seq->list (gen-seq:seq-map #'extract-entry entries))))
  0: (EXTRACT-ENTRY
      (("messaging"
        (("message" ("text" . "hola") ("seq" . 3227)
          ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
         ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
         ("sender" ("id" . "1695095647186162"))))
       ("time" . 1496071518212) ("id" . "474086316315717")))
  0: EXTRACT-ENTRY returned
       ((("message" ("text" . "hola") ("seq" . 3227)
          ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
         ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
         ("sender" ("id" . "1695095647186162"))))
  0: (EXTRACT-MESSAGING
      (("message" ("text" . "hola") ("seq" . 3227)
        ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
       ("timestamp" . 1496071517968) ("recipient" ("id" . "474086316315717"))
       ("sender" ("id" . "1695095647186162"))))
  0: EXTRACT-MESSAGING returned
       (("text" . "hola") ("seq" . 3227)
        ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u"))
((("text" . "hola") ("seq" . 3227)
  ("mid" . "mid.$cAAGvLYgmIaVihhT_EFcVM-1tP17u")))

但是当我这样做时,我失去了对术语 so 的惰性求值,我怎样才能在不失去泛型序列中的惰性求值的情况下实现它。

如果您的函数处理关联列表,您可能会使用如下函数检查它们是否确实获得了有效的关联列表:

(defun assert-valid-alist (alist)
  (assert (and (not (null alist))     ; not the empty list
               (listp alist)          ; it is a list
               (every #'consp alist)  ; every item is a cons
               (every (lambda (item)     
                        (symbolp (car item)))  ; every key should be a symbol
                      alist))
      (alist)
    "Not a valid assoc list: ~a" alist))

(defun get-age (person)
  (assert-valid-alist person)
  (cdr (assoc 'age person)))

示例:

CL-USER 106 > (get-age '((name . ute) (age . 34)))
34

CL-USER 107 > (get-age '(((name . ute) (age . 34))))

Error: Not a valid assoc list: (((NAME . UTE) (AGE . 34)))
  1 (continue) Retry assertion with new value for ALIST.
  2 (abort) Return to level 0.
  3 Return to top loop level 0.

这可能会降低性能,您可以在代码运行后删除它。