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?

3
  • 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, 2015 at 1:45
  • What @phils said. You have a problem here: (mapcar 'jj/factor codelist x).
    – Drew
    Jul 11, 2015 at 1:54
  • FYO, cl-mapcar takes arbitrary many lists (that's also the behavior of Common Lisp mapcar).
    – wvxvw
    Jul 11, 2015 at 10:21

1 Answer 1

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))))
3
  • Ahh cool. So apply-partially is the mathematical analogue of given a y defining f(x)=F(x,y). Thanks for the help! Jul 11, 2015 at 2:04
  • 1
    Or without apply-partially: (mapcar `(lambda (cd) (jj/factor ,x cd)) codelist).
    – Drew
    Jul 11, 2015 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, 2015 at 15:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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