Thursday, 24 November 2016

How to convert from kebab-case to camelCase in Clojure

Here are six functions that do that. Pick your favourite. It won't be the best performing one. Can you guess which that is? Scroll down for answer.
(defn kebab->camel1 [^String kebab]
  (->> kebab
       (reduce
        (fn [[buf ucase] c]
          (let [dash (= \- c)]
            [(if dash
               buf
               (conj buf (if ucase
                           (Character/toUpperCase c)
                           c)))
             dash]))
        [[] false])
       first
       (apply str)))

(defn kebab->camel2 [^String kebab]
  (->> (map
        (fn [cur prev] (if (= \- prev)
                         (Character/toUpperCase cur)
                         cur)) kebab (cons nil kebab))
       (filter #(not= \- %))
       (apply str)))

(defn kebab->camel3 [^String kebab]
  (loop [buf []
         ucase false
         rst kebab]
    (if (seq rst)
      (let [c (first rst)
            dash (= \- c)]
        (recur (if dash
                 buf
                 (conj buf (if ucase
                             (Character/toUpperCase c)
                             c)))
               dash
               (rest rst)))
      (apply str buf))))

(defn kebab->camel4 [^String kebab]
  (->> (map
        (fn [[prev cur]] (if (= \- prev)
                           (Character/toUpperCase cur)
                           cur)) (partition 2 1 (cons nil kebab)))

       (filter #(not= \- %))
       (apply str)))

(defn kebab->camel5 [^String kebab]
  (let [pv (volatile! nil)
        sb (StringBuilder. (count kebab))]
    (doseq [c kebab]
      (let [prior @pv
            dash (= \- c)]
        (vreset! pv dash)
        (when-not dash (.append
                        sb
                        (if prior
                          (Character/toUpperCase c) c)))))
    (.toString sb)))

(defn kebab->camel6 [^String kebab]
  (let [buf (.toCharArray kebab)
        len (count buf)]
    (loop [src 0 dst 0 ucase nil]
      (if (< src len)
        (let [c (aget buf src)
              dash (= \- c)]
          (when-not dash (aset buf dst (if ucase (Character/toUpperCase c) c)))
          (recur (inc src) (if dash dst (inc dst)) dash))
        (String. buf 0 dst)))))

To see which is best we do this

(let [kebab (->> (.ints (java.util.Random. 17)  0 31)
                 .iterator
                 iterator-seq
                 dedupe
                 (map #(nth "abcdefghijklmnopqrstuvwxyz-----" %))
                 (take 10000)
                 (apply str))
      time-fn (fn [f]
                (let [start (System/nanoTime)]
                  (f)
                  (/ (double (- (System/nanoTime) start)) 1000000.0)))]
  (for [f [kebab->camel1
           kebab->camel2
           kebab->camel3
           kebab->camel4
           kebab->camel5
           kebab->camel6]]
    [f (time-fn #(dotimes [_ 1000] (f kebab)))]))

and get results like these

([#function[user/kebab->camel1] 9064.630932]
 [#function[user/kebab->camel2] 11520.793361]
 [#function[user/kebab->camel3] 8440.000948]
 [#function[user/kebab->camel4] 20740.882337]
 [#function[user/kebab->camel5] 8455.671506]
 [#function[user/kebab->camel6] 99.724767])

Wednesday, 23 November 2016

How to get Structure and Interpretation of Computer Programs as an Info file on your system

sudo dnf install texi2html texinfo 
git clone https://github.com/webframp/sicp-info.git
cd sicp-info/
makeinfo --no-split sicp.texi -o sicp.info
gzip sicp.info
sudo install-info sicp.info.gz /usr/share/info/dir
sudo mv sicp.info.gz /usr/share/info

Monday, 14 November 2016

My crazy keyboard configuration

I have this in my .Xmodmap file.
  clear mod5
  clear mod4
  clear lock
  clear control
  keycode 49 = ISO_Level3_Shift
  keycode 66 = Menu NoSymbol Menu
  keycode 37 = Super_L NoSymbol Super_L
  keycode 133 = Control_L NoSymbol Control_L
  keycode 108 = Alt_L
  keycode 134 = Control_R NoSymbol Control_R
  add mod5 = ISO_Level3_Shift
  add mod4 = Super_L Hyper_R
  add control = Control_L Control_R
  keycode  10 = 1 exclam 1 exclam grave onesuperior exclamdown
  keycode  11 = 2 quotedbl 2 quotedbl notsign twosuperior trademark
  keycode  12 = 3 sterling 3 sterling brokenbar threesuperior copyright

It turns my Windows keys into Ctrl keys, my left Ctrl key into a Windows key, my Caps Lock key into a menu (▤) key, my AltGr key into a regular Alt key and it turns the key directly below Esc (I call it backtick) into an AltGr. To access the characters that were on that key I type my new AltGr + 1 or 2 or 3.

Why these changes?

  • I need a symmetrical ctrl key arrangement (on both my desktop and laptop).
  • I need a symmetrical alt key arrangement. (I'm an Emacs user.)
  • But I still need an AltGr key
  • I have customized Emacs to make the menu key very useful (following Xah Lee's advice), that's why I've moved it to the easy-to-reach Caps Lock position.

Thursday, 3 November 2016

How to create a Java (/Clojure/Scala) service on Linux

This is a sort of a continuation of an earlier post. In that post I said that you have to create a shell file called test-service or whatever in /etc/init.d/.

You will need Apache Jsvc. Your Java (or whatever JVM language) code will include a class that implements org.apache.commons.daemon.Daemon.

Here is what you should put in that shell file to make you JVM thing into a Linux service called test-service (test.daemon.Daemon is the class you wrote which implements org.apache.commons.daemon.Daemon).

#! /bin/sh                                                                                                                                                                                     
### BEGIN INIT INFO                                                                                                                                                                            
# Provides:     test-service                                                                                                                                                                   
# Required-Start:                                                                                                                                                                              
# Required-Stop:                                                                                                                                                                               
# Default-Start:     S                                                                                                                                                                         
# Default-Stop:                                                                                                                                                                                
# X-Start-Before:                                                                                                                                                                              
# Short-Description:                                                                                                                                                                           
# Description: test-service                                                                                                                                                                    
### END INIT INFO                                                                                                                                                                              
                                                                                                                                                                                               
                                                                                                                                                                                               
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64                                                                                                                                             
SVC_DIR=/home/mikey/test-service                                                                                                                                                               
                                                                                                                                                                                               
                                                                                                                                                                                               
case "$1" in                                                                                                                                                                                   
    start|"")                                                                                                                                                                                  
        /usr/bin/jsvc -wait 20 -cwd $SVC_DIR -pidfile $SVC_DIR/test-service.pid -outfile $SVC_DIR/out.log -errfile $SVC_DIR/err.log -cp $SVC_DIR/target/test-service-0.1-standalone.jar test.daemon.Daemon                                                                                                                                                                                   
        exit $?                                                                                                                                                                                
        ;;                                                                                                                                                                                     
    restart|reload|force-reload)                                                                                                                                                               
        echo "Error: argument '$1' not supported" >&2                                                                                                                                          
        exit 3                                                                                                                                                                                 
        ;;                                                                                                                                                                                     
    stop)                                                                                                                                                                                      
        /usr/bin/jsvc -wait 20 -stop -cwd $SVC_DIR -pidfile $SVC_DIR/test-service.pid -outfile $SVC_DIR/out.log -errfile $SVC_DIR/err.log -cp $SVC_DIR/target/test-service-0.1-standalone.jar test.daemon.Daemon                                                                                                                                                                             
        exit $?                                                                                                                                                                                
        ;;                                                                                                                                                                                     
    status)                                                                                                                                                                                    
        # No-op                                                                                                                                                                                
        ;;                                                                                                                                                                                     
    *)                                                                                                                                                                                         
        echo "Usage: test-service.sh [start|stop]" >&2                                                                                                                                         
        exit 3                                                                                                                                                                                 
        ;;                                                                                                                                                                                     
esac                                                                                                                                                                                           
                                                                                                                                                                                               
: