Decay Score Functions for Gnus

Decay Score Functions for Gnus

For a few weeks now, I’ve been using Gnus as my MUA. The main reason for leaving Mutt is the intriguing adaptive scoring Gnus provides, which seems to be especially tailored towards people like me that read mailing lists a lot. Gnus decays non-permanent scores once a day according to modest rules applied with the gnus-decay-score function. By default, every score entry up to 60 is reduced by a constant (of 3), values greater than 60 are downscaled by 5%. This results in a (almost) linear function that deals gently with literal scores. While there is nothing wrong with a linear function here, I’d like to restrict the outcome to a certain range.

The first alternative builds on the default decay function and multiplies the literal score with a factor derived from the exponentiation of the score with a negative constant. Thus the adapted score is still (almost) a linear function but shrinks output values quite a bit:

(defun gnus-decay-score-1 (score)
   (pcase score
     (`0 0)
     ((guard (< score 0)) (* score (expt (abs score) -0.2)))
     (_ (* score (expt score -0.2))))))

For comparison, the following function applies an increasing exponential decay to literal score values. This results in values that increase rapidly first, level off thereafter and essentially max out close to a defined upper limit:

(defun gnus-decay-score-2 (score)
  (let ((ulim 100)
        (exponent -0.12))
     (pcase score
       (`0 0)
       ((guard (< score 0)) (* -1 ulim (- 1 (expt (abs score) exponent))))
       (_ (* ulim (- 1 (expt score exponent))))))))

Plotting adapted scores from various decay functions for a sequence from -50 to 250, a range that encloses almost all my current score values, will reveal the differences:

set terminal pngcairo size 720,320 enhanced font 'Lato,10'

set auto y
set xtics 25
set tics out scale 0.4 nomirror
set style line 101 lc rgb '#8c8c8c' lt 1 lw 1
set border 3 front ls 101
set xlabel 'Literal Score'
set ylabel 'Adapted Score'
set key left nobox
set termoption dash

plot dat using 1:4 with lines lc rgb '#cd00cd' lt 5 lw 1 title 'gnus-decay-score-2',\
     dat using 1:3 with lines lc 3 lt 5 lw 1 title 'gnus-decay-score-1',\
     dat using 1:2 with lines lc rgb '#228b22' lt 3 lw 1 title 'gnus-decay-score',\
     dat using 1:1 with lines lc 1 lt 1 lw 1 title 'literal score'


Figure 1: Gnus Decay Score Function Comparison

NBA’s Free Agent’s Relative Salary Increase

NBA’s Free Agent’s Relative Salary Increase


While NBA’s salary cap continues to rise – 2016 every team has roughly $24 million more to spend, the projected cap for 2017 is something in-between $102 and $108 million – free agents (FA) take their opportunity to sign staggering contracts. The agreement of Mozgov and the Lakers for example attracted a lot of attention. But is he that bad of a deal for the LAL? I decided to not only trust in my own judgement, but do some simple dataset exploration.

It’s amazing how sometimes you can perform the whole exploratory data analysis life cycle inside Emacs – from the data retrieval to the publishing (as is done in this post). Initially I considered mentioning homoiconicity, since all my text is data – to be exact, though, the internal representation of an org-table is still a list and not what I look at in the buffer. Nonetheless, the excitement is quite similar. The process of getting messy data into shape took me a few minutes:

And these are the outcomes: Whiteside and Drummond are absolutely worth the dough, Conley is more of a surprise. Actually, there are a lot of big men on top of that list, will the small-ball dominance be a blip?

Table 1: NBA’s Free Agent’s Salary Comparison (Top 20)
Team Player Pos Age Contract (in y) Salary Total (in MM) Salary 2015-16 Salary 2016-17 Increase (in MM)
MIA Hassan Whiteside C 27 4 98 981348 24500000 +23.52
DET Andre Drummond C 23 5 130 3272091 26000000 +22.73
MEM Mike Conley G 29 5 153 9588426 30600000 +21.01
DAL Harrison Barnes F 24 4 95 3873398 23750000 +19.88
TOR DeMar DeRozan G 27 5 145 9500000 29000000 +19.50
WAS Bradley Beal G 23 5 120 5694674 24000000 +18.31
POR Allen Crabbe F 24 4 75 947276 18750000 +17.80
BOS Al Horford F/C 30 4 113 12000000 28250000 +16.25
ATL Kent Bazemore G/F 27 4 70 2000000 17500000 +15.50
ORL Bismack Biyombo C 24 4 72 3000000 18000000 +15.00
ORL Evan Fournier G/F 24 5 85 2288205 17000000 +14.71
POR Evan Turner G/F 28 4 70 3425510 17500000 +14.07
WAS Ian Mahinmi C 30 4 64 4000000 16000000 +12.00
CHA Nicolas Batum G/F 28 5 120 12235750 24000000 +11.76
DAL Dirk Nowitzki F 38 2 40 8333334 20000000 +11.67
MIA Tyler Johnson G 24 4 50 845059 12500000 +11.65
LAL Jordan Clarkson G 24 4 50 845059 12500000 +11.65
NOP Solomon Hill F 25 4 52 1358880 13000000 +11.64
HOU Ryan Anderson F 28 4 80 8500000 20000000 +11.50
LAL Timofey Mozgov C 30 4 64 4950000 16000000 +11.05

These are some teams I’m interested in that decided to offer their new signings a relative salary increase (with one exception). The table pretty much reflects the pecuniary space of these teams before the free agency:

Table 2: Average Salary Increase per signed FA
Team N FAs Average Increase
BOS 1 +16.25
MEM 4 +8.42
LAL 5 +7.12
MIA 7 +5.92
CHI 3 +2.72
SAS 3 +2.26
NYK 5 +0.92
GSW 5 +0.52
CLE 2 -2.00

Tag Cloud in Elisp

Tag Cloud in Elisp

While I went with manual links so far, I rediscovered tags in Org-mode to associate and filter headings in a non-hierarchical manner. While manually linking still seems important to me, finding (all) information on a given topic by tags is faster obviously. Several note-taking systems have incorporated tagging, a lot of them provide tag clouds. Something that’s missing in Org-mode. So I played around with the "special" display text-property which allows to change the text’s appearance. Some even implemented animations in Emacs for fun using this property. First, I came up with a way to retrieve and sort all the available tags in my file and store them in an alist:

(let ((tbl
       (with-current-buffer (find-file-noselect "~/slips/")
          (goto-char (point-min))
          (let (tags)
            (while (re-search-forward
                     "\\(?:.*?[ \t]\\)?:\\([[:alnum:]_@#%:]+\\):[ \t]*$")
                    nil t)
              (dolist (tag (org-split-string
                            (match-string-no-properties 1)
                (push tag tags)))
            (mapcar (lambda (x) (cons (car x) (length x)))
                    (seq-group-by 'identity tags)))))))
  (sort tbl (lambda (x y) (> (cdr x) (cdr y)))))

The tag cloud itself is a work in progress, the code may appear on Github in the near future though:

Clock into Recently Clocked Tasks with Ido

Clock into Recently Clocked Tasks with Ido

This will be short (and sweet). org-clock-in offers a list of recently clocked tasks to select from when called with one C-u (known as the universal-argument). Unfortunately, the task selection doesn’t use any completion engine. Therefore I prefer the following snippet, using ido for completion:

(defun bp/org-clock-in-select ()
  "Select a task to clock into from a list of recently clocked items."
  (let (res)
    (dolist (i org-clock-history)
          (org-base-buffer (marker-buffer i))
           (goto-char (marker-position i))
           (push `(,(org-get-heading 'notags) . ,i) res)))))
    (let* ((l (reverse (mapcar 'car res)))
           (task (alist-get (ido-completing-read "Recent Clocks: " l) res)))
      (when task
            (org-base-buffer (marker-buffer task))
           (goto-char (marker-position task))

Discover Destructuring Assignment in Elisp

Discover Destructuring Assignment in Elisp

LISt Processing in Emacs Lisp obviously involves a lot of juggling with lists and their elements. What else would be more convenient than generalizing the access and binding of list elements? Not only does the concept of destructuring assignment come along with code that is easier to write but also easier to read (terse, patterns that visually cue what elements are supposed to be assigned to variables). Alas, some features of Elisp have to be discovered. While pcase got its node in the Elisp Manual, neither is there an explanation what QPATTERN and UPATTERN mean nor are the related macros ever even mentioned. As if this wasn’t enough, the docstrings of pcase-let and its starred equivalent will leave the average Emacs user puzzled, pcase-dolist doesn’t even have one. This will hopefully change in subsequent versions of Emacs. For now, get ready to embark on a journey of discovery!

pcase is by far the most frequently used macro from pcase.el. What it does is pattern matching, a concept that goes beyond the scope of a blogpost. If you’re familiar with the Fibonacci Sequence, the following example is self-explanatory:

(defun fib (n)
  (pcase n
    (`0 1)
    (`1 1)
    (n (+ (fib (- n 1)) (fib (- n 2))))))
(mapcar 'fib (number-sequence 0 6))
(1 1 2 3 5 8 13)

Generally, pcase is used as a powerful conditional programming construct. Several examples can be found on this EmacsWiki page. Especially suited to the beforementioned destructuring is pcase-let:

    ((`(,spec ,month ,day ,name) (nth 3 holiday-general-holidays)))
  (princ (format "%s is on 2016-%d-%d" name month day))
"Valentine's Day is on 2016-2-14"

The practical advantage will become patently obvious when trying to do the same with let:

(let* ((l (nth 3 holiday-general-holidays))
       (spec (car l))
       (month (cadr l))
       (day (caddr l))
       (name (cadddr l)))
  (princ (format "%s is on 2016-%d-%d" name month day))))

pcase-let in its simplest form resembles Python’s poor man’s destructuring-bind, called tuple and list unpacking:

([a, b, c], d, e) = ([1, 1, 2], 3, 5)
print a + b if a < d else d

Probably even more interesting is pcase-dolist that iterates over the lists of a list:

(let ((l '()))
  (pcase-dolist (`(,spec ,month ,day . ,rest) holiday-general-holidays)
    (push (cons month day
                (if (stringp (car rest)) rest (cdr rest)))
  (nreverse l))
Table 1: Holidays common throughout the United States
Month Holiday
1 New Year's Day
1 Martin Luther King Day
2 Groundhog Day
2 Valentine's Day
2 President's Day
3 St. Patrick's Day
4 April Fools' Day
5 Mother's Day
5 Memorial Day
6 Flag Day
6 Father's Day
7 Independence Day
9 Labor Day
10 Columbus Day
10 Halloween
11 Veteran's Day
11 Thanksgiving

Digging even further into the library, you’ll discover a pcase-lambda. Yet, I’m still not sure what it does besides accepting pcase patterns. But I won’t worry for now, there is exactly ONE appearance of pcase-lambda in the Emacs sources.

Adding Mail-Abbrev Expansion to Org-MIME

Adding Mail-Abbrev Expansion to Org-MIME

As has been already mentioned, I overcome the temptation to use one of Emacs’ mail-clients. Yet still, I frequently use message-send-mail from within Emacs with a message-body composed with org-mime-subtree. You will find org-mime in the /contrib directory of your org-mode installation. Since manually filling the mail-header is redundant especially for buffers frequently (re)used, I set related properties to the current subtree that org-mime-send-subtree will parse before creating the email buffer. Unfortunately there is no built-in completion for addresses, which is why I use the following code. Apparently it’s wise to set alias_file in Mutt and mail-personal-alias-file in Emacs to the same file. It’s probably worth pointing out that I remove the commas introduced by define-mail-alias that separate the address from the definition in an alias.

(defvar org-mime-properties '("MAIL_TO" "MAIL_CC" "MAIL_BCC")
  "Properties `org-mime-send-subtree' parses.")

(setq mail-aliases-only
      ;; first build `mail-aliases', then create a string for `org-set-property'.
      (mapconcat 'car (build-mail-aliases) " "))

;; hook to replace the alias in the property field with name and email
(defun bp/mail-property-changed (property value)
  (let ((el (cdr (assoc value mail-aliases))))
    (when (and el (member property org-mime-properties))
       (replace-regexp-in-string "," "" el)))))

(nconc org-default-properties org-mime-properties)
(nconc org-global-properties
       `(("MAIL_TO_ALL"         . ,mail-aliases-only)
         ("MAIL_BCC_ALL"        . ,mail-aliases-only)
         ("MAIL_CC_ALL"         . ,mail-aliases-only)))
(add-to-list 'org-property-changed-functions

Custom File Order for Projectile

Custom File Order for Projectile

Recently I decided to write the occasional post in English. Obviously there are several reasons to do so as a non-native English blogger, probably the most common one being extending the audience. Since – the frequent visitor may have noticed (is there any?) – this blog lacks tracking tools and logfiles, I don’t really care about the reader (yes, talking about you). Hell, you cannot even leave comments, let alone „like“ it! Moving from Octopress to Pelican and having the removal of Mathjax and Datatables in my considerations, the blog strives not only to be puristic and elegant in appearance but also behind the scenes. Speaking of which, one year ago I put my hands on a copy of Thomas & Turner’s Clear and simple as the truth.1 Applying these writing instructions to this blog not only fits into the overall goal here, but it also serves my main purpose of writing posts in English: Write clear and simple to improve expressiveness (not only in English) and thought.

That being said, today’s post looks into one of my favorite Elisp libraries. Projectile is still my first choice when it comes to project management (though there is the standard library project.el coming up on the horizon, for now providing a project-find-regexp). I use it without the caching that is meant to adress indexing issues on Windows systems. The projectile-sort-order allows to settle on one of the sorting mechanisms provided by projectile-sort-files. For quite some time I used modification-time that almost met my expectations. Striving for a consistent development environment, I decided to apply the ido-file-extensions-order to projectile-find-file, eventually coming to know that ido-file-extension-lessp sorts by filename and extension:

(let ((ido-file-extensions-order '(".py" ".org" ".el")))
  (sort '("a.el" "" "") 'ido-file-extension-lessp))
("a.el" "" "")

So I decided to run my own thing. The following code requires seq.el, apparently I’m running emacs-major-version 25.

(defun projectile-sort-files (files)
  (pcase projectile-sort-order
    (`default files)
    (`recentf (projectile-sort-by-recentf-first files))
    (`recently-active (projectile-sort-by-recently-active-first files))
    (`modification-time (projectile-sort-by-modification-time files))
    (`access-time (projectile-sort-by-access-time files))
     (bp/projectile-ido-file-extensions-order files))))

(setq projectile-sort-order 'ido-file-extensions-order)

(defun bp/projectile-ido-file-extensions-order (files)
  "Sort FILES by `ido-file-extensions-order'."
  (let* ((ord
          (mapcar (lambda (s) (string-remove-prefix "." s))
                  (delq t ido-file-extensions-order)))
         (sorted (sort
                   (lambda (f) (member (file-name-extension f) ord)) files)
                  (lambda (a b)
                    (< (seq-position ord (file-name-extension a))
                       (seq-position ord (file-name-extension b)))))))
    (append sorted (seq-difference files sorted))))

← Newer    Older →