Scheme - Map function for applying a function to e

2019-04-02 10:15发布

问题:

I'm trying to write a mapping function in scheme that applies a function to each value in a nested list.

For example, (map number? '(3 (2 A) 2 Z) should return (#t (#t #f) #t #f)

Here's what I have so far:

(define (map fun lst)
    (if (null? lst) '()
        (if (list? (car lst)) 
            (cons (map fun (car lst)) (map fun (cdr lst)))
                 (cons (fun (car lst)) (map fun (cdr lst))))))

It works if the nested list is at the front of the list. For example (map number? '((3 A) 2 Z)) correctly returns ((#t #f) #t #f)

The problem occurs when the nested list occurs after another element in the original list. For example (map number? '(3 A (2 Z))) incorrectly returns (#t #f #f) [The result should be (#t #f (#t #f))]

How can I change my algorithm to correct this?

回答1:

Here's my solution---it's seriously cheap in that it reuses the built-in map, using the decorator pattern. (I know, Scheme programs using design patterns? :-O)

(define (deep-map f l)
  (define (deep x)
    (cond ((null? x) x)
          ((pair? x) (map deep x))
          (else (f x))))
  (map deep l))

This can be "simplified" even further by using a named let:

(define (deep-map f l)
  (let deep ((x l))
    (cond ((null? x) x)
          ((pair? x) (map deep x))
          (else (f x)))))

(The two snippets of code are not identical, but for this question, both will work the same if given a list as input.)

The null? and pair? checks (both O(1)) are used in order to avoid using list? (which is O(n)).



回答2:

Your code is correct, except that it's way too verbose than it could be. Hint: you need to worry about only two cases: whether lst is a pair? or not. That's all. In other words, your code assumes that the input is always a list, but it could be simplified to accept anything, and do the special recursive thing when it's a pair.

As for the problem that you're seeing -- my guess is that you're using Racket, and therefore you're running against an odd case. If this is not the case then you can stop reading here.

The thing is that in Racket the function itself will compile before the binding to map is made, therefore the map calls are not really recursive, but instead they're just calls to the builtin map. Later on, map is (re-)bound to the resulting function, but the recursive calls were already compiled. If you enter the same definition twice, you'll see that it starts working again. The way to solve this is to just not work at the REPL, and instead always write definitions in a file that starts with some #lang <something>.