2

@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
3

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
  • 1
    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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.