Disciples
Disciples

Reputation: 703

How to specify arguments for substitute in mapcar?

For example, I have a list of "strings" : ("First" "second" "third.").
And I need to replace all "s" to "a" -> (("Firat")...etc I've found very beautiful function called substitute, but it works only for one sequence.
Can I use substitute in mapcar ? Something like this:

(mapcar 'substitute #\d #\o '(("test") ("wow") ("waow")))

Upvotes: 2

Views: 495

Answers (3)

kennytilton
kennytilton

Reputation: 1064

Beyond a flat list one could consider recursion, to handle the general nesting case:

(labels ((my-sub (s)
           (typecase s
             (string (substitute #\d #\o s))
             (list (mapcar #'my-sub s))
             (otherwise s))))
  (my-sub '(("I" "like" "my" "dog.")
        ("My" ("dog" "likes" "me") "too."))))

Upvotes: 2

Daniel Jour
Daniel Jour

Reputation: 16156

I'm adding to jkiiski's very nice answer one final possibility to specify arguments to substitute in this case, though it's probably not what you need:

(mapcar #'substitute '(#\x #\x #\x) '(#\a #\b #\c) '("abcd" "abcd" "abcd"))
;; => ("xbcd" "axcd" "abxd")

Here for every i up to the lowest length of any passed list, mapcar passes the ith element of the first list as first argument, the ith element of the second as second argument and the i th element as third argument to substitute. This works because mapcar takes one or any number of lists.

Using this only makes sense if you want to perform different substitutions on (almost) each of the strings.

Upvotes: 3

jkiiski
jkiiski

Reputation: 8411

You can wrap a LAMBDA around the function:

(mapcar (lambda (string) (substitute #\d #\o string)) '("test" "wow" "waow"))
;=> ("test" "wdw" "wadw")

Or you can use a helper function called CURRY (which is not part of the Common Lisp standard, but is available in Alexandria for example).

(ql:quickload :alexandria)
(use-package :alexandria)
(mapcar (curry #'substitute #\d #\o) '("test" "wow" "waow"))
;=> ("test" "wdw" "wadw")

For multiple strings in sublists, you can use nested mapcar/lambda:

(mapcar (lambda (sentence) 
          (mapcar (lambda (string) 
                    (substitute #\d #\o string))
                  sentence))
        '(("I" "like" "my" "dog.") ("My" "dog" "likes" "me" "too.")))
;=> (("I" "like" "my" "ddg.") ("My" "ddg" "likes" "me" "tdd."))

The inner LAMBDA could be changed to a CURRY too:

(mapcar (lambda (sentence) 
          (mapcar (curry #'substitute #\d #\o) sentence))
        '(("I" "like" "my" "dog.") ("My" "dog" "likes" "me" "too.")))

Or even:

(mapcar (curry #'mapcar (curry #'substitute #\d #\o))
        '(("I" "like" "my" "dog.") ("My" "dog" "likes" "me" "too.")))

Upvotes: 5

Related Questions