Why is my elisp function not accepting a second parameter?

@Juancho gave a great answer to a question I asked a few days ago. My question is about how to modify his elisp code.

I have an org table

#+begin_src emacs-lisp :results none
(defun jj/factor (code)
(cond ((eq (string-to-char code) ?P) .05)
(t 0)))

(defun jj/factorlist (codelist)
(mapcar 'jj/factor codelist))

(defun jj/dotproduct (a b)
(apply '+ (mapcar* '* (jj/factorlist a) (mapcar 'string-to-number b))))
#+end_src

|        |        |   P1 |   P2 |   Q1 |   Q2 |   E1 |   E2 |    F |
|--------+--------+------+------+------+------+------+------+------|
|        | Totals |   20 |   40 |   50 |   45 |  100 |  100 |  200 |
|--------+--------+------+------+------+------+------+------+------|
| John   |   0.10 |    1 |    1 |    1 |    1 |    1 |    1 |    1 |
| Paul   |   1.00 |   10 |   10 |   10 |   10 |   10 |   10 |   10 |
| George |  10.00 |  100 |  100 |  100 |  100 |  100 |  100 |  100 |
| Ringo  | 100.00 | 1000 | 1000 | 1000 | 1000 | 1000 | 1000 | 1000 |
#+TBLFM: @3\$2..@>\$2='(jj/dotproduct (list @1\$3..@1\$>) (list \$3..\$>));%.2f

The function jj/dotproduct takes the entries in the columns marked P, multiplies them by .05, and adds them up in the column marked Totals.

Now, I'd like to modify jj/dotproduct so it takes another input x where x any letter. This would allow me to sum the columns marked Q, E, and F separately. I've tried modifying it as follows

#+begin_src emacs-lisp :results none
(defun jj/factor (code x)
(cond ((eq (string-to-char code) (string-to-char x)) .05)
(t 0)))

(defun jj/factorlist (codelist x)
(mapcar 'jj/factor codelist x))

(defun jj/dotproduct (x a b)
(apply '+ (mapcar* '* (jj/factorlist a x) (mapcar 'string-to-number b))))
#+end_src

|        |        |   P1 |   P2 |   Q1 |   Q2 |   E1 |   E2 |    F |
|--------+--------+------+------+------+------+------+------+------|
|        | Totals |   20 |   40 |   50 |   45 |  100 |  100 |  200 |
|--------+--------+------+------+------+------+------+------+------|
| John   |   0.00 |    1 |    1 |    1 |    1 |    1 |    1 |    1 |
| Paul   |   0.00 |   10 |   10 |   10 |   10 |   10 |   10 |   10 |
| George |   0.00 |  100 |  100 |  100 |  100 |  100 |  100 |  100 |
| Ringo  |   0.00 | 1000 | 1000 | 1000 | 1000 | 1000 | 1000 | 1000 |
#+TBLFM: @3\$2..@>\$2='(jj/dotproduct "P" (list @1\$3..@1\$>) (list \$3..\$>));%.2f

This, however, gives the wrong output. Any ideas what I've done wrong and how I can fix my code?

• See C-h f mapcar. It takes two arguments -- a function and a sequence. You're now trying to pass it an additional argument, which it can't use. – phils Jul 11 '15 at 1:45
• What @phils said. You have a problem here: (mapcar 'jj/factor codelist x). – Drew Jul 11 '15 at 1:54
• FYO, cl-mapcar takes arbitrary many lists (that's also the behavior of Common Lisp mapcar). – wvxvw Jul 11 '15 at 10:21

As per my comment, you're trying to pass more arguments to mapcar than it takes. You must only pass it a function and a sequence.

An elegant way to solve your problem is to use apply-partially to obtain the function you need. Note that we need x to be the first argument to jj/factor.

(defun jj/factor (x code)
(cond ((eq (string-to-char code) (string-to-char x)) .05)
(t 0)))

(defun jj/factorlist (codelist x)
(mapcar (apply-partially 'jj/factor x) codelist))

(defun jj/dotproduct (x a b)
(apply '+ (mapcar* '* (jj/factorlist a x) (mapcar 'string-to-number b))))
• Ahh cool. So apply-partially is the mathematical analogue of given a y defining f(x)=F(x,y). Thanks for the help! – Brian Fitzpatrick Jul 11 '15 at 2:04
• Or without apply-partially: (mapcar `(lambda (cd) (jj/factor ,x cd)) codelist). – Drew Jul 11 '15 at 4:04
• I meant to add that apply-partially is usable only if the arguments you want to apply the function to partially are the first parameters it accepts, and in the same order. Using lambda (or a defun) lets you apply the function partially to any subset of arguments, supplied in any order. – Drew Jul 11 '15 at 15:15