In the October 1999 Communications of the ACM Lutz Prechelt
had a attention-grabbing article entitled Evaluating
Java vs. C/C++ Efficiency Issues to Interpersonal Issues
requested 38 programmers to implement versions of a program in C, C++, or
Java. The conclusions confirmed that Java was once 3 or 4 times slower than C
or C++, but that the variance between programmers was once greater than the
variance between languages, suggesting that one might possess to exercise
extra time on coaching programmers in desire to arguing over language
dedication. (Or, suggesting that it’s in all probability you’ll perchance most likely level-headed hire the precise programmers and
finish a ways flung from the misguided ones.) The variance for Java was once lower than for C or
C++. (Cynics might insist that Java forces you to jot down uniformly slack
applications.) I applaud this line of work, and hope that extra studies of
this kind will be done.

It turns out my hopes had been answered. First, Prechelt printed one other
that covers Tcl, Python, Perl, and Rexx. Also,
Ron Garret (nee Erann Gat) did
prepare-up peep
whereby he requested programmers to jot down Prechelt’s
take a look at
in Articulate. His outcomes inform that the ensuing Articulate
applications ran sooner on average than C, C++ or Java applications (even supposing
the quickest Articulate program was once no longer as hasty as the quickest C program),
and that Articulate applications took less model time than the diversified

I didn’t opt on half within the peep, but after I saw it, I wrote my
version in Articulate. It took me about 2 hours (when compared with a large range of two
to eight.5 hours for the diversified Articulate programmers within the peep, 3 to 25 for
C/C++ and 4 to 63 for Java) and I performed up with 45 non-commentary
non-easy strains (when compared with a large range of 51 to 182 for Articulate, and 107
to 614 for the diversified languages). (Which attain that some Java programmer
was once spending 13 strains and 84 minutes to indulge in the efficiency of
every line of my Articulate program.)

Here is my program:

;; Peter Norvig - Programming Challange from Erann Gat:
;; Given a listing of phrases and a listing of phone numbers, safe the total ways in which
;; every phone quantity might be expressed as a listing of phrases.
;; Plod: (major "observe-listing-file-title" "phone-quantity-file-title")

(defvar *dict* nil 
  "A hash table mapping a phone quantity (integer) to a listing of phrases from the 
  enter dictionary that indulge in that quantity.")

(defun major (&no longer mandatory (dict "dict") (nums "nums") (dict-dimension 100))
  "Read the enter file ¨DICT and load it into *dict*.  Then for every line in 
  NUMS, print the total translations of the quantity into a sequence of phrases,
  consistent with the foundations of translation."
  (setf *dict(load-dictionary dict dict-dimension))
  (with-delivery-file (in nums)
    (loop for num = (be taught-line in nil) while num enact
          (print-translations num (opt on-if-no longer #'digit-char-p num)))))

(defun print-translations (num digits &no longer mandatory (originate 0) (phrases nil))
  "Print every conceivable translation of NUM into a string of phrases.  DIGITS
  ought to be WORD with non-digits eradicated.  On recursive calls, START is the
  situation in DIGITS at which to computer screen the following observe, and WORDS is the listing
  of phrases stumbled on for (subseq DIGITS 0 START).  So if START will get to the cease of
  DIGITS, then we possess a resolution in WORDS.  In every other case, for every prefix of
  DIGITS, sight within the dictionary for observe(s) that blueprint to the price of the
  prefix (computed incrementally as N), and for every such observe strive to lengthen
  the resolution with a recursive name.  There are two complications: (1) the
  principles insist that to boot to dictionary phrases, it's in all probability you'll perchance most likely exercise a single 
  digit within the output, but no longer two digits in a row. Also (and this looks 
  foolish) it's in all probability you'll perchance most likely't possess a digit in a situation where any observe might appear.
  I handle this with the variable FOUND-WORD; if it is fraudulent after the loop, 
  and basically the most most up-to-date observe is no longer any longer a digit, strive a recursive name that pushes a 
  digit. (2) The diversified complication is that the evident blueprint of mapping 
  strings to integers would blueprint R to 2 and ER to 02, which of course is 
  the a similar integer as 2.  Which potential that reality we prepend a 1 to every quantity, and R 
  becomes 12 and ER becomes 102."
  (if (>= originate (length digits))
      (format t "~a:~{ ~a~}~%" num (reverse phrases))
      (let ((stumbled on-observe nil)
            (n 1)) ; leading zero content
        (loop for i from originate under (length digits) enact
              (setf n (+ 10 n) (nth-digit digits i)))
              (loop for observe in (gethash n *dict*) enact
                 (setf stumbled on-observe t)
                 (print-translations num digits (+ 1 i) (cons observe phrases))))
        (when (and (no longer stumbled on-observe) (no longer (numberp (first phrases))))
           (print-translations num digits (+ originate 1) 
                               (cons (nth-digit digits originate) phrases))))))

(defun load-dictionary (file dimension)
  "Compose a hashtable from the file of phrases (one per line).  Takes a mark
  for the preliminary hashtable dimension.  Every key is the phone quantity for a observe;
  every stamp is a listing of phrases with that phone quantity."
  (let ((table (derive-hash-table :take a look at #'eql :dimension dimension)))
    (with-delivery-file (in file)
      (loop for observe = (be taught-line in nil) while observe enact
        (push observe (gethash (observe->quantity observe) table))))

(defun observe->quantity (observe)
  "Translate a observe (string) into a phone quantity, consistent with the foundations."
  (let ((n 1)) ; leading zero content
    (loop for i from 0 under (length observe) 
          for ch = (char observe i) enact
          (when (alpha-char-p ch) (setf n (+ 10 n) (char->digit ch)))))

(defun nth-digit (digits i) 
  "The i-th ingredient of a character string of digits, as an integer 0 to 9."
  (- (char-code (char digits i)) #.(char-code #)))

(defun char->digit (ch)
  "Convert a character to a digit consistent with the phone quantity principles."
  (ecase (char-downcase ch)
    ((#e) 0)
    ((#j #n #q) 1)
    ((#r #w #x) 2)
    ((#d #s #y) 3)
    ((#f #t) 4)
    ((#a #m) 5)
    ((#c #i #v) 6)
    ((#b #ok #u) 7)
    ((#l #o #p) 8)
    ((#g #h #z) 9)))

Two notes: (1) At the beginning, I idea I had overwhelmed the diversified Articulate
programmers (and all individuals else) by ending in 1.5 hours, but then I
realized that I had cheated: the instructions mentioned to code as that you might
professionally, and which attain I might level-headed commentary my work. It took 30 minutes
extra to position within the comments and neat up a minute bit. (2) I was once
lucky in that after I was once virtually done, I realized that I had a worm: I
might no longer distinguish between the quantity 123 and 0123. I was once about to
swap numbers to strings (that is, 123 to “123” and 0123 to “0123”),
which would possess taken 15 minutes or so, when I realized that I might
repair the worm with a one-character swap: swap the initialization of
n from 0 to 1, so that 123 is represented as 1123 and 0123 as
10123. With that repair, all the issues gave the influence to work beautiful.


This article is on hand in Czech, thanks to Daniela Milton.

Peter Norvig