替换(嵌套)中的符号?在LISP中列出

时间:2015-02-02 17:07:31

标签: common-lisp

我提供的格式如下:

(test '((Q H)(A D)(J C)(Q S)(3 S)))

目的是搜索列表并将符号J Q K和A替换为相应的数字11 12 13和14.目前我的功能是这个(对不起):

    (defun test (hand)
  (cond ((equal (first (first hand)) 'J)
         (setf (first (first hand)) '11))
        ((equal (first (first hand)) 'Q)
         (setf (first (first hand)) '12))
        ((equal (first (first hand)) 'K)
         (setf (first (first hand)) '13))
        ((equal (first (first hand)) 'A)
         (setf (first (first hand)) '14))
        (t (print '(It's ogre now))))
  (cond ((equal (first (second hand)) 'J)
         (setf (first (second hand)) '11))
        ((equal (first (second hand)) 'Q)
         (setf (first (second hand)) '12))
        ((equal (first (second hand)) 'K)
         (setf (first (second hand)) '13))
        ((equal (first (second hand)) 'A)
         (setf (first (second hand)) '14))
        (t (print '(It's ogre now))))
  (cond ((equal (first (third hand)) 'J)
         (setf (first (third hand)) '11))
        ((equal (first (third hand)) 'Q)
         (setf (first (third hand)) '12))
        ((equal (first (third hand)) 'K)
         (setf (first (third hand)) '13))
        ((equal (first (third hand)) 'A)
         (setf (first (third hand)) '14))
        (t (print '(It's ogre now))))
  (cond ((equal (first (fourth hand)) 'J)
         (setf (first (fourth hand)) '11))
        ((equal (first (fourth hand)) 'Q)
         (setf (first (fourth hand)) '12))
        ((equal (first (fourth hand)) 'K)
         (setf (first (fourth hand)) '13))
        ((equal (first (fourth hand)) 'A)
         (setf (first (fourth hand)) '14))
        (t (print '(It's ogre now))))
  (cond ((equal (first (fifth hand)) 'J)
         (setf (first (fifth hand)) '11))
        ((equal (first (fifth hand)) 'Q)
         (setf (first (fifth hand)) '12))
        ((equal (first (fifth hand)) 'K)
         (setf (first (fifth hand)) '13))
        ((equal (first (fifth hand)) 'A)
         (setf (first (fifth hand)) '14))
         (t (print '(It's ogre now))))
  (print hand))

我确信有一种更清洁的方法可以做到这一点。有人能指出我正确的方向吗?

2 个答案:

答案 0 :(得分:1)

如果您想用相应的数字替换所有出现的J / Q / K / A,您可以使用SUBST函数:

(subst 11 'J (subst 12 'Q (subst 13 'K (subst 14 'A hand))))

或者,如果你有更多的东西可以替代,那么看起来会更好看:

(loop for (letter value) in '((J 11) (Q 12) (K 13) (A 14))
      for hand% = (subst value letter (or hand% hand))
      finally (return hand%))

答案 1 :(得分:0)

让我们这样做。 [dons code-review hat]


首先,你的程序将改变它的输入,这被认为是不好的风格。

CL-USER> (defun test (hand)
  ...
  (print hand))
TEST
CL-USER> (defparameter *a-hand* '((Q H)(A D)(J C)(Q S)(3 S)))
*A-HAND*
CL-USER> (test *a-hand*)

(IT 'S OGRE NOW) 
((12 H) (14 D) (11 C) (12 S) (3 S)) 
((12 H) (14 D) (11 C) (12 S) (3 S))
CL-USER> *a-hand*
((12 H) (14 D) (11 C) (12 S) (3 S))
CL-USER> 

这是因为你在整个地方使用setf来改变你的论点。执行该突变的替代方法是建立一个新的对列表,其中第一个元素由其数字表示替换。构建一个新的list并应用了一些转换恰好是Common Lisp中mapcar函数的目的。为了应用该策略,我们需要弄清楚您对传入列表的每个元素所执行的操作。从上面来看,模式应该是显而易见的:

...
(cond ((equal (first (first hand)) 'J)
         (setf (first (first hand)) '11))
        ((equal (first (first hand)) 'Q)
         (setf (first (first hand)) '12))
        ((equal (first (first hand)) 'K)
         (setf (first (first hand)) '13))
        ((equal (first (first hand)) 'A)
         (setf (first (first hand)) '14))
        (t (print '(It's ogre now))))
...

因此。让我们得到一个单独的函数,它接受list,有时用一些数字替换它的第一个元素。

(lambda (lst)
  (cond ((equal (first lst) 'J) 
         (cons '11 (cdr lst)))
        ((equal (first lst) 'Q)
         (cons '12 (cdr lst)))
        ((equal (first lst) 'K)
         (cons '13 (cdr lst)))
        ((equal (first lst) 'A)
         (cons '14 (cdr lst)))
        (t lst)))

请注意,我们已将(first (first hand))替换为对我们的参数(first lst)的引用,并且我们现在使用cons来构建一个在第一个位置具有替换元素的新列表。现在,我们可以通过调用hand将此功能映射到mapcar

(defun test (hand)
  (print (mapcar (lambda (lst)
                   (cond ((equal (first lst) 'J) 
                          (cons '11 (cdr lst)))
                         ((equal (first lst) 'Q)
                          (cons '12 (cdr lst)))
                         ((equal (first lst) 'K)
                          (cons '13 (cdr lst)))
                         ((equal (first lst) 'A)
                          (cons '14 (cdr lst)))
                         (t lst)))
                 hand)))

这不再对你的论点造成明显的副作用。

CL-USER> (defun test (hand)
       (print (mapcar (lambda (lst)
                (cond ((equal (first lst) 'J) 
                   (cons '11 (cdr lst)))
                  ((equal (first lst) 'Q)
                   (cons '12 (cdr lst)))
                  ((equal (first lst) 'K)
                   (cons '13 (cdr lst)))
                  ((equal (first lst) 'A)
                   (cons '14 (cdr lst)))
                  (t lst)))
              hand)))
STYLE-WARNING: redefining COMMON-LISP-USER::TEST in DEFUN
TEST
CL-USER> (defparameter *a-hand* '((Q H)(A D)(J C)(Q S)(3 S)))
*A-HAND*
CL-USER> (test *a-hand*)

((12 H) (12 D) (12 C) (12 S) (12 S)) 
((12 H) (12 D) (12 C) (12 S) (12 S))
CL-USER> *a-hand*
((Q H) (A D) (J C) (Q S) (3 S))
CL-USER> 

您正在打印输出(以及原始中的一些It's ogre now行)而不是仅仅返回它。正如您在REPL中看到的那样,这将导致显示多个副本,如果您想稍后编写test功能,则打印将不会为您做任何事情。如果你需要print输出,那么将这段逻辑留给调用者并且只关注在函数本身中进行替换可能是个更好的主意。

(defun test (hand)
  (mapcar (lambda (lst)
            (cond ((equal (first lst) 'J) 
                   (cons '11 (cdr lst)))
                  ((equal (first lst) 'Q)
                   (cons '12 (cdr lst)))
                  ((equal (first lst) 'K)
                   (cons '13 (cdr lst)))
                  ((equal (first lst) 'A)
                   (cons '14 (cdr lst)))
                  (t lst)))
          hand))

我们仍然以lambda形式重复发生,我们传递给mapcar。特别是,每个子句都涉及cons新值到输入的cdr(或同义rest)。由于Common Lisp中的流控制结构也是返回值的函数,因此我们可以将它们组成一些重复次数。

(defun test (hand)
  (mapcar (lambda (lst)
            (cons
             (cond ((equal (first lst) 'J) '11)
                   ((equal (first lst) 'Q) '12)
                   ((equal (first lst) 'K) '13)
                   ((equal (first lst) 'A) '14)
                   (t (car lst)))
             (cdr lst)))
          hand))

cond可能不是最好在这里使用的东西。由于我们总是进行相同的检查,并且该检查恰好是相等的,我们可以使用case

(defun test (hand)
  (mapcar (lambda (lst)
            (cons
             (case (first lst)
               (J '11)
               (Q '12)
               (K '13)
               (A '14)
               (t (first lst)))
             (cdr lst)))
          hand))

或者,您可以定义值表,并查找其中的第一个元素。

(defun test (hand)
  (let ((table '(J 11 Q 12 K 13 A 14)))
    (mapcar 
     (lambda (lst)
       (cons (or (getf table (car lst)) (car lst))
             (cdr lst)))
     hand)))

最后,您不需要引用Common Lisp数字。他们已经进行了自我评估。

(defun test (hand)
  (mapcar (lambda (lst)
            (cons
             (case (first lst)
               (J 11)
               (Q 12)
               (K 13)
               (A 14)
               (t (first lst)))
             (cdr lst)))
          hand))

如果您刚刚开始编程,我建议您通过these tutorials获取Racket,而不是立即潜入Common Lisp。