Lisp格式化多项式

3

我将稀疏多项式表示为(系数,指数对)列表。例如:

'((1 2) (3 6) (-20 48)) => x^2 + 3x^6 - 20x^48

我对Lisp格式化不太熟悉,但是已经发现了一些非常方便的工具,比如(format nil "~:[+~;-~]" (> 0 coefficient))可以将系数的符号转换为文本(我知道,那可能不太符合惯用说法)。

然而,在格式化单项式时会出现某些显示问题。例如,以下情况都应该成立:

(1 0) => 1x^0 => 1    (reducible)
(1 1) => 1x^1 => x    (reducible)
(1 2) => 1x^2 => x^2  (reducible)
(2 0) => 2x^0 => 2    (reducible)
(2 1) => 2x^1 => 2x   (reducable)
(2 2) => 2x^2 => 2x^2 (this one is okay)

我在想是否有一种方法可以不使用大量的ifcond宏来完成这个任务 - 只使用单个format模式即可。所有的功能都正常工作,但需要美化术语(FormatPolynomialHelper3中的最后一行应该做到这一点)。

(defun FormatPolynomial (p)
    "Readably formats the polynomial p."
    ; The result of FormatPolynomialHelper1 is a list of the form (sign formatted),
    ; where 'sign' is the sign of the first term and 'formatted' is the rest of the
    ; formatted polynomial. We make this a special case so that we can print a sign
    ; attached to the first term if it is negative, and leave it out otherwise. So,
    ; we format the first term to be either '-7x^20' or '7x^20', rather than having
    ; the minus or plus sign separated by a space.
    (destructuring-bind (sign formatted-poly) (FormatPolynomialHelper1 p)
        (cond
            ((string= formatted-poly "") (format nil "0"))
            (t                           (format nil "~:[~;-~]~a" (string= sign "-") formatted-poly)))))

; Helpers

(defun FormatPolynomialHelper1 (p)
    (reduce #'FormatPolynomialHelper2 (mapcar #'FormatPolynomialHelper3 p) :initial-value '("" "")))

(defun FormatPolynomialHelper2 (t1 t2)
    ; Reduces ((sign-a term-a) (sign-b term-b)) => (sign-b "term-b sign-a term-a"). As
    ; noted, this accumulates the formatted term in the variable t2, beginning with an
    ; initial value of "", and stores the sign of the leading term in the variable t1.
    ; The sign of the leading term is placed directly before the accumulated formatted
    ; term, ensuring that the signs are placed correctly before their coefficient. The
    ; sign of the the leading term of the polynomial (the last term that is processed)
    ; is available to the caller for special-case formatting.
    (list
        (first t2)
        (format nil "~@{~a ~}" (second t2) (first t1) (second t1))))

(defun FormatPolynomialHelper3 (tm)
    ; Properly formats a term in the form "ax^b", excluding parts of the form if they
    ; evaluate to one. For example, 1x^3 => x^3, 2x^1 => 2x, and 3x^0 => 3). The list
    ; is in the form (sign formatted), denoting the sign of the term, and the form of
    ; the term state above (the coefficient have forced absolute value).
    (list
        (format nil "~:[+~;-~]" (> 0 (first tm)))
        (format nil "~a~@[x^~a~]" (abs (first tm)) (second tm))))

编辑:正确地指出,输出结果不应包含逻辑。也许我对我的问题提出了过于具体的要求。下面是一个正确格式化多项式的逻辑 - 但我正在寻找更简洁、更易读和更符合Lisp习惯用法的解决方案(这只是我写Lisp的第三天)。

(defun FormatPolynomialHelper3 (tm)
    ; Properly formats a term in the form "ax^b", excluding parts of the form if they
    ; evaluate to one. For example, 1x^3 => x^3, 2x^1 => 2x, and 3x^0 => 3). The list
    ; is in the form (sign formatted), denoting the sign of the term, and the form of
    ; the term state above (the coefficient have forced absolute value).
    (list
        (format nil "~:[+~;-~]"   (> 0 (first tm)))
        (cond
            ((= 0 (second tm))      (format nil "~a" (abs (first tm))))
            ((= 1 (abs (first tm))) (cond
                ((= 1 (second tm))  (format nil "x"))
                (t                  (format nil "x^~a" (second tm)))))
            ((= 1 (second tm))      (format nil "~ax" (abs (first tm))))
            (t                      (format nil "~ax^~a" (abs (first tm)) (second tm))))))
1个回答

5

答案:

我不会将这个逻辑放进FORMAT语句中。这样只会增加维护工作或者使你的代码被加密。好的Lisp代码应该是自说明的,FORMAT语句则从来不是自说明的。

在打印之前,我会先简化多项式。例如,删除每个被零乘以的项。

((0 10) (1 2)) -> ((1 2))

如果乘数是1,则可以在普通的CONDCASE语句中进行测试。

同时,确保永远不要在自制数据结构中使用CARCDRFIRSTSECOND。多项式的组成部分应该大部分通过自我记录的函数来访问,隐藏大部分实现细节。

以下是没有使用FORMAT的示例代码:

示例代码:

(defun term-m (term)
  (first term))

(defun term-e (term)
  (second term))

(defun simplify-polynomial (p)
  (remove-if #'zerop (sort p #'> :key #'term-e)
             :key #'term-m))

(defun write-term (m e start-p stream)
  ; sign or operator
  (cond ((and (minusp m) start-p)
         (princ "-" stream))
        ((not start-p)
         (princ (if (plusp m) " + " " - ") stream)))
  ; m
  (cond ((not (= (abs m) 1))
         (princ (abs m) stream)))
  (princ "x" stream)
  ; e
  (cond ((not (= 1 e))
         (princ "^" stream)
         (princ e stream))))

(defun write-polynomial (p &optional (stream *standard-output*))
  (loop for (m e) in (simplify-polynomial p)
        for start-p = t then nil
        do (write-term m e start-p stream)))

使用示例:

CL-USER 14 > (write-polynomial '((1 2) (3 6) (-20 48)))
-20x^48 + 3x^6 + x^2

术语 '(1 0)'(2 0) 格式化为 x^02x^0,但它们应该分别为 12 - efritz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接