| 1 |
Ddoc |
|---|
| 2 |
|
|---|
| 3 |
$(COMMUNITY Lisp vs. Java... D?, |
|---|
| 4 |
|
|---|
| 5 |
<center> |
|---|
| 6 |
$(I by Lionello Lunesu, Oct. 18th 2006) |
|---|
| 7 |
</center> |
|---|
| 8 |
|
|---|
| 9 |
<p>Two weeks ago, Josh Stern posted an |
|---|
| 10 |
$(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Lisp_vs._C_not_off-topic_42717.html, interesting link) |
|---|
| 11 |
on the |
|---|
| 12 |
<a href="http://www.digitalmars.com/d/archives/digitalmars/D/index.html">digitalmars.D newsgroup</a>. |
|---|
| 13 |
The link pointed to a |
|---|
| 14 |
<a href="http://userpages.umbc.edu/~bcorfm1/C++-vs-Lisp.html">page describing a programming challenge</a> |
|---|
| 15 |
that was originally used in a |
|---|
| 16 |
study in which 38 C, C++ and Java programmers were asked to write versions of a |
|---|
| 17 |
program to compare Java and C++ efficiency. |
|---|
| 18 |
</p> |
|---|
| 19 |
|
|---|
| 20 |
<p>Many other programmers had taken up the challenge since. In <a href="http://www.norvig.com/java-lisp.html"> |
|---|
| 21 |
one such comparison</a>, Lisp turned out to be a clear winner over Java and |
|---|
| 22 |
C++ when comparing development time and lines of code, with the record standing |
|---|
| 23 |
at about 2 hours, with 45 non-comment non-blank lines. For C++, the shortest |
|---|
| 24 |
development time was 3 hours and the |
|---|
| 25 |
shortest program had 107 lines of code. |
|---|
| 26 |
</p> |
|---|
| 27 |
<p>With some time to spare, I thought I should give it a try myself. Although I'm a |
|---|
| 28 |
C++ programmer myself (and have been for 10 years now), I thought it would be a |
|---|
| 29 |
great opportunity to test my skills in the <a href="http://www.digitalmars.com/d/">D |
|---|
| 30 |
programming language</a>.</p> |
|---|
| 31 |
<p>I had not looked at the other implementations and only read the <a href="http://www.flownet.com/ron/papers/lisp-java/"> |
|---|
| 32 |
original problem statement</a>. It took me <strong>1 hour and 15 minutes</strong>, |
|---|
| 33 |
from reading the statement to finishing the program. The program was just 55 |
|---|
| 34 |
lines long (remember, the |
|---|
| 35 |
shortest C++ entry |
|---|
| 36 |
had 107 lines), not counting blank |
|---|
| 37 |
lines, comments, assertions, unittests, contracts and trailing curly brackets |
|---|
| 38 |
(similar to Lisp). |
|---|
| 39 |
</p> |
|---|
| 40 |
<p>The program basically had to do this: read all words from a dictionary file; for |
|---|
| 41 |
each letter there was a corresponding digit (like the letters on a phone); |
|---|
| 42 |
using this mapping, read phone numbers from another text file and print all |
|---|
| 43 |
possible combinations of words for that phone number. Check the link above for |
|---|
| 44 |
more details.</p> |
|---|
| 45 |
<p>What made implementing this task so easy in D? I just happened to have every |
|---|
| 46 |
construct I needed at hand. Some of the goodies used, are:</p> |
|---|
| 47 |
$(UL |
|---|
| 48 |
$(LI Assertions, contracts and unittests) |
|---|
| 49 |
$(LI Built-in arrays and strings, with easy slicing) |
|---|
| 50 |
$(LI Built-in associative array) |
|---|
| 51 |
$(LI Garbage collection) |
|---|
| 52 |
$(LI Type deduction and foreach) |
|---|
| 53 |
$(LI Nested functions) |
|---|
| 54 |
$(LI Returning (reference to) a array from a function) |
|---|
| 55 |
) |
|---|
| 56 |
<p>During the actual programming I did not have to stop and think about anything. |
|---|
| 57 |
The program evolved automatically, once I thought of the actual container for |
|---|
| 58 |
the dictionary. It turned out that the $(SINGLEQUOTE winning) Lisp version was storing the |
|---|
| 59 |
dictionary in a similar manner.</p> |
|---|
| 60 |
<h2>Compiling</h2> |
|---|
| 61 |
<p>To test the program yourself, first download the <a href="http://www.flownet.com/ron/papers/lisp-java/dictionary.txt"> |
|---|
| 62 |
dictionary.txt</a> and <a href="http://www.flownet.com/ron/papers/lisp-java/input.txt"> |
|---|
| 63 |
input.txt</a>. Then, <a href="http://www.digitalmars.com/d/download.html">download |
|---|
| 64 |
the latest D compiler</a>. To compile, |
|---|
| 65 |
copy-paste the D code into a newly created file |
|---|
| 66 |
phoneno.d, and compile it with: |
|---|
| 67 |
</p> |
|---|
| 68 |
$(CONSOLE |
|---|
| 69 |
dmd -run phoneno.d |
|---|
| 70 |
) |
|---|
| 71 |
|
|---|
| 72 |
--------------------- |
|---|
| 73 |
// Created by Lionello Lunesu and placed in the public domain. |
|---|
| 74 |
// This file has been modified from its original version. |
|---|
| 75 |
// It has been formatted to fit your screen. |
|---|
| 76 |
module phoneno; // optional |
|---|
| 77 |
import std.stdio; // writefln |
|---|
| 78 |
import std.ctype; // isdigit |
|---|
| 79 |
import std.stream; // BufferedFile |
|---|
| 80 |
|
|---|
| 81 |
// Just for readability (imagine char[][][char[]]) |
|---|
| 82 |
alias char[] string; |
|---|
| 83 |
alias string[] stringarray; |
|---|
| 84 |
|
|---|
| 85 |
/// Strips non-digit characters from the string (COW) |
|---|
| 86 |
string stripNonDigit( in string line ) |
|---|
| 87 |
{ |
|---|
| 88 |
string ret; |
|---|
| 89 |
foreach(uint i, c; line) { |
|---|
| 90 |
// Error: std.ctype.isdigit at C:\dmd\src\phobos\std\ctype.d(37) |
|---|
| 91 |
// conflicts with std.stream.isdigit at C:\dmd\src\phobos\std\stream.d(2924) |
|---|
| 92 |
if (!std.ctype.isdigit(c)) { |
|---|
| 93 |
if (!ret) |
|---|
| 94 |
ret = line[0..i]; |
|---|
| 95 |
} |
|---|
| 96 |
else if (ret) |
|---|
| 97 |
ret ~= c; |
|---|
| 98 |
} |
|---|
| 99 |
return ret?ret:line; |
|---|
| 100 |
} |
|---|
| 101 |
|
|---|
| 102 |
unittest { |
|---|
| 103 |
assert( stripNonDigit("asdf") == "" ); |
|---|
| 104 |
assert( stripNonDigit("\'13-=2 4kop") == "1324" ); |
|---|
| 105 |
} |
|---|
| 106 |
|
|---|
| 107 |
/// Converts a word into a number, ignoring all non alpha characters |
|---|
| 108 |
string wordToNum( in string word ) |
|---|
| 109 |
{ |
|---|
| 110 |
// translation table for the task at hand |
|---|
| 111 |
const char[256] TRANSLATE = |
|---|
| 112 |
" " // 0 |
|---|
| 113 |
" 0123456789 " // 32 |
|---|
| 114 |
" 57630499617851881234762239 " // 64 |
|---|
| 115 |
" 57630499617851881234762239 " |
|---|
| 116 |
" " |
|---|
| 117 |
" " |
|---|
| 118 |
" " |
|---|
| 119 |
" "; |
|---|
| 120 |
string ret; |
|---|
| 121 |
foreach(c; cast(ubyte[])word) |
|---|
| 122 |
if (TRANSLATE[c] != ' ') |
|---|
| 123 |
ret ~= TRANSLATE[c]; |
|---|
| 124 |
return ret; |
|---|
| 125 |
} |
|---|
| 126 |
|
|---|
| 127 |
unittest { |
|---|
| 128 |
// Test wordToNum using the table from the task description. |
|---|
| 129 |
assert( "01112223334455666777888999" == |
|---|
| 130 |
wordToNum("E | J N Q | R W X | D S Y | F T | A M | C I V | B K U | L O P | G H Z")); |
|---|
| 131 |
assert( "01112223334455666777888999" == |
|---|
| 132 |
wordToNum("e | j n q | r w x | d s y | f t | a m | c i v | b k u | l o p | g h z")); |
|---|
| 133 |
assert( "0123456789" == |
|---|
| 134 |
wordToNum("0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9")); |
|---|
| 135 |
} |
|---|
| 136 |
|
|---|
| 137 |
void main( string[] args ) |
|---|
| 138 |
{ |
|---|
| 139 |
// This associative array maps a number to an array of words. |
|---|
| 140 |
stringarray[string] num2words; |
|---|
| 141 |
|
|---|
| 142 |
foreach(string word; new BufferedFile("dictionary.txt" ) ) |
|---|
| 143 |
num2words[ wordToNum(word) ] ~= word.dup; // must dup |
|---|
| 144 |
|
|---|
| 145 |
/// Finds all alternatives for the given number |
|---|
| 146 |
/// (should have been stripped from non-digit characters) |
|---|
| 147 |
stringarray _FindWords( string numbers, bool digitok ) |
|---|
| 148 |
in { |
|---|
| 149 |
assert(numbers.length > 0); |
|---|
| 150 |
} |
|---|
| 151 |
out(result) { |
|---|
| 152 |
foreach (a; result) |
|---|
| 153 |
assert( wordToNum(a) == numbers ); |
|---|
| 154 |
} |
|---|
| 155 |
body { |
|---|
| 156 |
stringarray ret; |
|---|
| 157 |
bool foundword = false; |
|---|
| 158 |
for (uint t=1; t<=numbers.length; ++t) { |
|---|
| 159 |
auto alternatives = numbers[0..t] in num2words; |
|---|
| 160 |
if (!alternatives) |
|---|
| 161 |
continue; |
|---|
| 162 |
foundword = true; |
|---|
| 163 |
if (numbers.length > t) { |
|---|
| 164 |
// Combine all current alternatives with all alternatives |
|---|
| 165 |
// of the rest (next piece can start with a digit) |
|---|
| 166 |
foreach (a2; _FindWords( numbers[t..$], true ) ) |
|---|
| 167 |
foreach(a1; *alternatives) |
|---|
| 168 |
ret ~= a1 ~ " " ~ a2; |
|---|
| 169 |
} |
|---|
| 170 |
else |
|---|
| 171 |
ret ~= *alternatives; // append these alternatives |
|---|
| 172 |
} |
|---|
| 173 |
// Try to keep 1 digit, only if we're allowed and no other |
|---|
| 174 |
// alternatives were found |
|---|
| 175 |
// Testing "ret.length" makes more sense than testing "foundword", |
|---|
| 176 |
// but the other implementations seem to do just this. |
|---|
| 177 |
if (digitok && !foundword) { //ret.length == 0 |
|---|
| 178 |
if(numbers.length > 1) { |
|---|
| 179 |
// Combine 1 digit with all altenatives from the rest |
|---|
| 180 |
// (next piece can not start with a digit) |
|---|
| 181 |
foreach (a; _FindWords( numbers[1..$], false ) ) |
|---|
| 182 |
ret ~= numbers[0..1] ~ " " ~ a; |
|---|
| 183 |
} |
|---|
| 184 |
else |
|---|
| 185 |
ret ~= numbers[0..1]; // just append this digit |
|---|
| 186 |
} |
|---|
| 187 |
return ret; |
|---|
| 188 |
} |
|---|
| 189 |
|
|---|
| 190 |
/// (This function was inlined in the original program) |
|---|
| 191 |
/// Finds all alternatives for the given phone number |
|---|
| 192 |
/// Returns: array of strings |
|---|
| 193 |
stringarray FindWords( string phone_number ) |
|---|
| 194 |
{ |
|---|
| 195 |
if (!phone_number.length) |
|---|
| 196 |
return null; |
|---|
| 197 |
// Strip the non-digit characters from the phone number, and |
|---|
| 198 |
// pass it to the recursive function (leading digit is allowed) |
|---|
| 199 |
return _FindWords( stripNonDigit(phone_number), true ); |
|---|
| 200 |
} |
|---|
| 201 |
|
|---|
| 202 |
// Read the phone numbers |
|---|
| 203 |
foreach(string phone; new BufferedFile("input.txt" ) ) |
|---|
| 204 |
foreach(alternative; FindWords( phone ) ) |
|---|
| 205 |
writefln(phone, ": ", alternative ); |
|---|
| 206 |
} |
|---|
| 207 |
--------------------- |
|---|
| 208 |
|
|---|
| 209 |
) |
|---|
| 210 |
|
|---|
| 211 |
Macros: |
|---|
| 212 |
TITLE=Lisp vs. Java... D? |
|---|
| 213 |
WIKI=LispVsJavaVsD |
|---|
| 214 |
COPYRIGHT= |
|---|