Compare two lists and return false if they are not

2019-07-22 09:49发布

问题:

i would like to ask you for help to complete code below with condition which is testing if lists ws and vs are not equal. If they are not equal so return text false(#f) else process code below. I stared with fulfilling variables len1 and len2 which are counting length of both lists. When i run it i am getting this error: lambda: no expression after a sequence of internal definitions in: lambda What i am doing wrong?

(define (weighted-sum . ws)
  (define (sub . vs)
    (let ((len1 (length ws)) (len2 (length vs)))
    (if (not (equal? (len1 len2) '#f))
    (foldl
     (lambda (i j res) (+ res (* i j)))
     0
     ws vs)))
   sub)

Thanks for help.

回答1:

length is almost always an anti-pattern in Scheme.

length is a O(n) operation, which is called twice, then you call another O(n) operation, foldl, resulting in a O(3n) process for weighted-sum - far from the ideal minimum O(n). foldl is a nice candidate for many linear computations, but because of the length-matching requirement, you've created a bit of a square-peg-in-a-round-hole situation.

Using a named-let and match*, we write weighted-sum as a O(n) computation -

#lang racket

(define ((weighted-sum . ws) . vs) ;; curried form syntactic sugar 
  (let loop ((acc 0)
             (ws ws)
             (vs vs))
    (match* (ws vs)
      ;; both lists have at least one value
      [((list w ws ...) (list v vs ...))
       (loop (+ acc (* w v))
             ws
             vs)]
      ;; both lists are empty
      [((list) (list))
       acc]
      ;; any other case
      [(_ _)
       #f])))

Of course match* is a pretty fancy macro, so I'll show you how to rewrite weighted-sum using a simple cond expression. Get your logical reasoning hat ready: the order of the condition clauses is very important here -

(define ((weighted-sum . ws) . vs)
  (let loop ((acc 0)
             (ws ws)
             (vs vs))
    (cond
      ;; both lists are empty
      [(and (null? ws)
            (null? vs))
       acc]
      ;; at least one list is empty
      [(or (null? ws)
           (null? vs))
       #f]
      ;; inductive: both lists have at least one value
      [else
       (loop (+ acc (* (car ws)
                       (car vs)))
             (cdr ws)
             (cdr vs))])))

Both programs have the same output -

((weighted-sum 1 2 3) 1 2 3)
;; 14

((weighted-sum 1 2 3) 1 2)
;; #f

((weighted-sum 1 2) 1 2 3)
;; #f

((weighted-sum))
;; 0


回答2:

Erase )) after #f . Add )) after len1 len2), and it'll work. (not quite, but close(*))

#f is self-evaluating, you don't need to quote it. Indent the (foldl ...) form which became a part of the if expression now.

Lastly, (if (not A) #f B) is the same as (if A B #f) is the same as (and A B).

You are correct in checking that the lengths of both lists, the carried (sic) and the expected, are equal. I don't see why the lists themselves should be equal, though. They shouldn't, as far I can tell.

(weighted-sum list-of-weights) creates a procedure expecting a list of numbers to calculate its weighted sum using the previously supplied weights.


(*) The corrected code, after a few more fixes, is:

(define (weighted-sum . ws)
  (define (sub . vs)
    (let ((len1 (length ws)) (len2 (length vs)))
      (and (equal? len1 len2)
        (foldl
          (lambda (i j res) (+ res (* i j)))
          0
          ws vs))))
   sub)

It is highly advisable to install e.g. Racket and use its editor to see and correct the parentheses mismatches etc.



标签: scheme racket