List Length in Schema

Hi, I'm trying to write a program that lists the lists to check if they are equal in size and return #t if there are any.

So, for example, if I were to write (list-counter? '((1 2 3) (4 5 6) (7 8 9))), the program will return #t and (list-counter?' ((1 2 3) (4 5 6) (7 8))) will return #f.

So far, here's what I did:

(define list-counter? (lambda (x) (if (list? x) (if (list?(car x)) (let (l (length (car x)))) (if (equal? l (length(car x)))) (list-counter?(cdr x)) ) ) ) ) ) 

I think I'm wrong after I set the length l to the length of the first list. Any help would be appreciated.

+4
source share
3 answers

There are several ways to solve this problem. For example, manually and step by step:

 (define (all-lengths lists) (if (null? lists) '() (cons (length (car lists)) (all-lengths (cdr lists))))) (define (all-equal? head lengths) (if (null? lengths) true (and (= head (car lengths)) (all-equal? head (cdr lengths))))) (define (list-counter? lists) (let ((lengths (all-lengths lists))) (all-equal? (car lengths) (cdr lengths)))) 

Let me explain the above procedures. I divide the problem into two stages: first create a new list with the length of each sublist - which makes all-lengths . Then compare the first item in the list with the rest of the items and see if they are all equal - what does all-equal? . Finally, list-counter? wraps it all together, calling both previous procedures with the correct parameters.

Or even simpler (and shorter) using list procedures (higher order procedures):

 (define (list-counter? lists) (apply = (map length lists))) 

To understand the second solution, note that all-lengths and all-equal? are special cases of more general procedures. When we need to create a new list with the result of applying the procedure to each of the elements of another list, we use map . And when we need to apply the procedure ( = in this case) to all elements of the list at the same time, we use apply . And this is exactly what the second version of list-counter? .

+2
source

Can you write an all-equal? function all-equal? in the following way:

 (define (all-equal? list) ;; (all-equal? '()) -> #t ;; (all-equal? '(35)) -> #t ;; (all-equal? '(2 3 2)) -> #f (if (or (null? list) (null? (cdr list))) #t (reduce equal? list) )) 

then do:

 (all-equal? (map length listOfLists)) 

Alternatively, you can:

 (define (lists-same-size? list-of-lists) (if (== (length listOfLists) 0) #t (let* (( firstLength (length (car listOfLists)) ) ( length-equal-to-first? (lambda (x) (== (length x) firstLength)) ) ) (reduce and #t (map length-equal-to-first? listOfLists)) ) ))) 

What this says: if the list length is 0, our operator is indefinite, otherwise we fix the first element of the list length (in the 'else' part of the if -clause), put it in the closure defined by let syntactic sugar (actually lambda), and use it to define the function length-equal-to-first? .

Unfortunately, reduce not lazy. We would really like to avoid calculating list lengths if we find that only one is not equal. Thus, to be more effective, we could do:

  ... (let* ... ( all-match? ;; lazy (lambda (pred list) (if (null? list) #t (and (pred (first list)) (all-match? (cdr list))) ;;^^^^^^^^^^^^^^^^^^^ stops recursion if this is false )) ) ) (all-match? length-equal-to-first? listOfLists) ) ))) 

Please note that all-match? already efficiently defined for you using the MIT scheme (list-search-positive list pred) or (for-all? list pred) or in Racket as andmap


Why write so long?

You are forced to write base case because your acronym does not have a canonical element, since it relies on the first element, and list manipulation in most languages ​​is not very effective. You would have the same problem in other languages ​​like Python. In case this helps:

second method:

 if len(listOfLists)==0: return True else: firstLength = len(listOfLists[0]) return all(len(x)==firstLength for x in listOfLists) 

However, the first method is much easier to write in any language, because it reduces this problem by ignoring basic cases.

first method:

 if len(listOfLists)<2: return True else: return reduce(lambda a,b: a==b, listOfLists) 
+1
source

It may seem a little strange, but I think it is easy.

Start the list by creating a new list containing the length of each (contained) list, i.e. card length.
Run the constructed list of lengths, comparing the head with the others, return #t if they are all the same as the head. Return false as soon as it does not match the head.

0
source

Source: https://habr.com/ru/post/1402782/


All Articles