Learn and Compare is a project to gain basic competency in a number of programming languages, demonstrate basic programming patterns in each, and compare them for utility, productivity, aesthetics, etc.
Program | Console | Web | GUI | TOPS-20 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
C# | Common Lisp | Pascal | Fortran | C | Perl | REXX | Python | Ruby | JavaScript | Ruby on Rails | TCL/TK | Fortran | ||
1. | Verbum: Print text message. | using System; namespace VerbumS { public class VerbumS { public static void Main (string[] args) { Console.WriteLine ("In principio erat Verbum"); } } } |
(format t "In principio erat Verbum~%") |
program verbum; begin writeln ('In principio erat Verbum'); end. |
program verbum write (*,*) 'In principio erat Verbum' stop end |
#include <stdio.h> int main () { printf ("In principio erat Verbum\n"); return 0; } |
print "In principio erat Verbum\n"; |
/* REXX */ say "In principio erat Verbum" |
print ("In principio erat Verbum") |
puts "In principio erat Verbum" |
<!DOCTYPE html> <html> <body> <script> document.write('In principio erat Verbum'); </script> </body> </html> |
pack [button .b -text "In principio erat Verbum" -command exit] |
program verbum write (5,*) 'In principio erat Verbum' stop end |
|
2. | Arginp: Print string from argument or input. | using System; namespace ArginpS { public class ArginpS { public static void Main (string[] args) { string buffer; if (args.Length < 1) { Console.Write("> "); buffer = Console.ReadLine(); } else { buffer = String.Join(" ", args); } Console.WriteLine(buffer); } } } |
(format t "~A~%" (or #+CLISP (car *args*) #+SBCL (cadr *posix-argv*) (progn (princ "> ") (finish-output) (read-line)))) |
program arginp; var s : string; begin if paramcount () > 0 then writeln (paramstr (1)) else begin write ('> '); readln (s); writeln (s); end; end. |
program arginp character*80 arg integer len, stat call get_command_argument (1, arg, len, stat) if (arg.eq.' ') then write (*,10) 10 format (' > ',$) read (*,*) arg end if write (*,*) arg stop end |
#include <stdio.h> #include <string.h> #define MAXLEN 80 int main (int argc, char *argv[]) { char buffer[MAXLEN+1]; buffer[MAXLEN] = '\0'; if (argc > 1) strncpy (buffer, argv[1], MAXLEN); else { printf ("> "); fgets (buffer, MAXLEN, stdin); if (strlen (buffer) < MAXLEN) buffer[strlen (buffer)-1] = '\0'; } printf ("%s\n", buffer); return 0; } |
if ($#ARGV >= 0) { print join (' ', @ARGV), "\n"; } else { print "> "; $input = <>; print $input; } |
/* REXX */ if arg() > 0 then say arg(1) else do ok = charout(, '> ') parse pull inp say inp end |
import sys if len (sys.argv) > 1: print (sys.argv[1]) else: inp = input ("> ") print (inp) |
if ARGV.length > 0 puts ARGV[0] else print "> " inp = gets puts inp end |
<!DOCTYPE html> <html><body><script> const url = new URL (window.location); if (url.searchParams.get ('input') != null) { document.write (url.searchParams.get ('input')); } else { document.write ( '<form><label for="input">> </label>' + '<input type="text" id="input" name="input"></form>' ); } </script></body></html> |
program arginp * FORTRAN-20 does not support passing command line arguments to user * programs. character*80 inp write (5,10) 10 format (x,'> ',$) read (5,20) inp 20 format (a80) write (5,*) inp stop end |
||
3. | Numrom: Convert number to Roman numeral. | using System; namespace NumromS { public class NumromS { public static void Main(string[] args) { int[] SymVal = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; string[] Symbol = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"}; int num; string romnum = ""; num = (args.Length > 0) ? Convert.ToInt32(args[0]) : 0; if (num < 1 || num > 4999) throw new ArgumentOutOfRangeException($"Invalid input: {num}; Argument must be in range 1-4999"); for (int i = 0; i < SymVal.Length; ++ i) { while (num >= SymVal[i]) { romnum += Symbol[i]; num -= SymVal[i]; } } Console.WriteLine(romnum); } } } |
(setq rn-symbol (make-array 13 :element-type 'string :initial-contents '("M" "CM" "D" "CD" "C" "XC" "L" "XL" "X" "IX" "V" "IV" "I"))) (setq rn-value (make-array 13 :element-type 'integer :initial-contents '(1000 900 500 400 100 90 50 40 10 9 5 4 1))) (let* ((number-string (or #+CLISP (car *args*) #+SBCL (cadr *posix-argv*) (progn (princ "> ") (finish-output) (read-line)))) (number (ignore-errors (parse-integer number-string))) (romnum "")) (if (not number) (error "Invalid input: ~s" number-string)) (if (or (< number 1) (> number 4999)) (error "Number out of range (1-4999): ~s" number)) (dotimes (s 13) (let ((sym (aref rn-symbol s)) (val (aref rn-value s))) (loop while (>= number val) do (setq romnum (concatenate 'string romnum sym)) (setq number (- number val))))) (format t "~a~%" romnum)) |
program numrom; {$mode objfpc} uses sysutils; const SYMBOL_CNT = 13; SYMBOL_VALUE: array [1 .. SYMBOL_CNT] of integer = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1); SYMBOL: array [1 .. SYMBOL_CNT] of string = ('M','CM','D','CD','C','XC','L','XL','X','IX','V','IV','I'); var i: integer; number: longint; number_str, romnum: string; begin if paramCount () > 0 then begin number_str := paramStr (1); try number := strtoint (number_str); except on e: econverterror do begin writeln ('Invalid input: ', number_str); exit; end; end; end else number := 0; if (number > 0) and (number <= 4999) then begin romnum := ''; for i := 1 to SYMBOL_CNT do begin while number >= SYMBOL_VALUE[i] do begin romnum := romnum + SYMBOL[i]; number := number - SYMBOL_VALUE[i]; end; end; writeln (romnum); end else writeln ('Input out-of-range (1-4999): ', number); end. |
program numrom implicit none integer len, stat, num, i, p, symval(13), symlen(13) character*3 symbol(13) character*16 romnum character*80 arg data (symval(i),i=1,13) /1000,900,500,400,100,90,50,40,10,9,5,4,1/ data (symbol(i),i=1,13) /'M','CM','D','CD','C','XC','L','XL','X', + 'IX','V','IV','I'/ data (symlen(i),i=1,13) /1,2,1,2,1,2,1,2,1,2,1,2,1/ call get_command_argument (1, arg, len, stat) if (arg.eq.' ') then write (*,10) 10 format (x,'> ',$) read (*,20) num 20 format (i4) else read (arg,*) num end if p = 1 if (num.lt.1 .or. num.gt.4999) then write (*,*) 'Invalid input: ',num else do i=1,13 do while (num.ge.symval(i)) romnum(p:p+symlen(i)) = symbol(i) p = p + symlen(i) num = num - symval(i) end do end do end if write (*,*) romnum stop end |
#include <stdio.h> #include <stdlib.h> #include <string.h> #define SYMCNT 13 #define MAXLEN 16 int main (int argc, char *argv[]) { int symval[SYMCNT] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; char symbol[SYMCNT][3] = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"}; int num, i; char romnum[MAXLEN+1] = ""; num = (argc > 1) ? atoi (argv[1]) : 0; if (argc <= 1 || strlen (argv[1]) > 4 || num < 1 || num > 4999) { printf ("Invalid input: %s\n", argv[1]); exit (EXIT_FAILURE); } for (i = 0; i < SYMCNT; ++ i) { while (num >= symval[i]) { strcat (romnum, symbol[i]); num -= symval[i]; } } printf ("%s\n", romnum); return 0; } |
$num = $#ARGV == 0 ? $ARGV[0] : 0; die "Invalid input: $ARGV[0]\n" if ($num =~ /\D/ || $num < 1 || $num > 4999); @valsym = (1000, "M", 900, "CM", 500, "D", 400, "CD", 100, "C", 90, "XC", 50, "L", 40, "XL", 10, "X", 9, "IX", 5, "V", 4, "IV", 1, "I"); $romnum = ""; while ($#valsym > -1) { $val = shift @valsym; $sym = shift @valsym; while ($num >= $val) { $romnum .= $sym; $num -= $val; } } print "$romnum\n"; |
/* REXX */ num = arg(1) if verify(num, '0123456789') > 0 | num < 1 | num > 4999 then do say 'Invalid input:' num exit end valsym = 1000 M 900 CM 500 D 400 CD 100 C 90 XC 50 L 40 XL 10 X 9 IX 5 V 4 IV 1 I romnum = '' do while words(valsym) > 0 parse var valsym val sym valsym do while num >= val romnum = romnum || sym num = num - val end end say romnum |
import sys import re if len (sys.argv) > 1: arg = sys.argv[1] else: arg = input ("> ") isNum = re.search ('^[0-9]+$', arg) if isNum: num = int (arg) if not isNum or num < 1 or num > 4999: sys.exit ("Invalid input: " + arg) symval = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1] symbol = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"] romnum = "" for i in range (len (symval)): while num >= symval[i]: romnum += symbol[i] num -= symval[i] print (romnum) |
num = ARGV.length > 0 ? ARGV[0].to_i : 0 abort ("Invalid input: #{ARGV[0]}") if num < 1 || num > 4999 symval = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1] symbol = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"] romnum = "" symval.each_with_index do |val, i| while num >= val romnum.concat(symbol[i]) num -= val end end puts romnum |
<!DOCTYPE html> <html><body><script> const symVal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; const symbol = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']; const url = new URL (window.location); let number = url.searchParams.get ('input'); let romNum = ""; if (number == null) { document.write ( '<form><label for="input">> </label>' + '<input type="text" id="input" name="input"></form>' ); } else if (isNaN (number) || parseInt (number) != number || number < 1 || number > 4999) { document.write ('Invalid input: ' + number); } else { for (let i = 0; i < symVal.length; ++ i) { while (number >= symVal[i]) { romNum += symbol[i]; number -= symVal[i]; } } document.write (romNum); } </script></body></html> |
program numrom implicit none integer num, i, p, symval(13), symlen(13) character*3 symbol(13) character*16 romnum data (symval(i),i=1,13) /1000,900,500,400,100,90,50,40,10,9,5,4,1/ data (symbol(i),i=1,13) /'M','CM','D','CD','C','XC','L','XL','X', + 'IX','V','IV','I'/ data (symlen(i),i=1,13) /1,2,1,2,1,2,1,2,1,2,1,2,1/ p = 1 write (5,10) 10 format (x,'> ',$) read (5,20) num 20 format (i4) if (num.lt.1 .or. num.gt.4999) then write (5,*) 'Invalid input: ',num else do i=1,13 do while (num.ge.symval(i)) romnum(p:p+symlen(i)) = symbol(i) p = p + symlen(i) num = num - symval(i) end do end do end if write (5,*) romnum stop end |
||
4. | Romnum: Convert Roman numeral to number. | program romnum; uses sysutils; const SYMBOL_CNT = 7; SYMBOL = 'MDCLXVI'; SYMBOL_VALUE: array [1..SYMBOL_CNT] of integer = (1000, 500, 100, 50, 10, 5, 1); var number, value, prev_value, p, s : integer; roman_numeral : string; begin if ParamCount > 0 then roman_numeral := UpperCase (ParamStr (1)) else begin writeln ('Usage: romnum ROMAN_NUMERAL'); exit; end; number := 0; prev_value := SYMBOL_VALUE[1]; for p := 1 to Length (roman_numeral) do begin for s := 1 to SYMBOL_CNT do if roman_numeral[p] = SYMBOL[s] then begin value := SYMBOL_VALUE[s]; number := number + value; if value > prev_value then number := number - 2 * prev_value; prev_value := value; break; end; end; writeln (number); end. |
$romnum = $#ARGV == 0 ? uc $ARGV[0] : ""; die "Invalid input: $ARGV[0]\n" if (length ($romnum) == 0 || $romnum =~ /[^MDCLXVI]/); %symval = (M => 1000, D => 500, C => 100, L => 50, X => 10, V => 5, I => 1); $num = 0; $prevsym = 'M'; while (length ($romnum) > 0) { $symbol = substr $romnum, 0, 1; $num += $symval{$symbol}; if ($symval{$symbol} > $symval{$prevsym}) { $num -= (2 * $symval{$prevsym}); } $prevsym = $symbol; $romnum = substr $romnum, 1; } print "$num\n"; |
/* REXX */ parse upper arg romnum if romnum = '' | verify(romnum, 'MDCLXVI') > 0 then do say 'Invalid input:' arg(1) exit end symval.M = 1000 symval.D = 500 symval.C = 100 symval.L = 50 symval.X = 10 symval.V = 5 symval.I = 1 num = 0 prevsym = M do length(romnum) symbol = left(romnum, 1) num = num + symval.symbol if symval.symbol > symval.prevsym then num = num - 2 * symval.prevsym prevsym = symbol romnum = substr(romnum, 2) end say num |
import sys import re if len (sys.argv) > 1: arg = sys.argv[1] else: arg = input ('> ') romnum = arg.upper () if not re.search ('^[MDCLXVI]+$', romnum): sys.exit ('Invalid input: ' + arg) symval = {'M': 1000, 'D': 500, 'C': 100, 'L': 50, 'X': 10, 'V': 5, 'I': 1} num = 0 prevsym = 'M' for p in range (len (romnum)): sym = romnum[p] num += symval[sym] if symval[sym] > symval[prevsym]: num -= (2 * symval[prevsym]) prevsym = sym print (num) |
romnum = ARGV.length > 0 ? ARGV[0].upcase : "0" abort ("Invalid input: #{ARGV[0]}") if romnum =~ /[^MDCLXVI]/ symval = {"M" => 1000, "D" => 500, "C" => 100, "L" => 50, "X" => 10, "V" => 5, "I" => 1} num = 0 prevsym = "M" for p in 0 .. (romnum.length - 1) sym = romnum[p] num += symval[sym] if symval[sym] > symval[prevsym] then num -= (2 * symval[prevsym]) end prevsym = sym end puts num |
<!DOCTYPE html> <html><body><script> const symVal = {M:1000, D:500, C:100, L:50, X:10, V:5, I:1}; const url = new URL (window.location); let romNum = url.searchParams.get ('input'); if (romNum == null) { document.write ( '<form><label for="input">> </label>' + '<input type="text" id="input" name="input"></form>' ); } else if (/[^MDCLXVI]/i.test (romNum)) { document.write ('Invalid input: ' + romNum); } else { let number = 0; let prevSym = 'M'; for (let symbol of romNum.toUpperCase().split('')) { number += symVal[symbol]; if (symVal[symbol] > symVal[prevSym]) { number -= (2 * symVal[prevSym]); } prevSym = symbol; } document.write (number); } </script></body></html> |
|||||||
5. | Hodie: Convert date to Roman format. | |||||||||||||
6. | Calendarium: Print year calendar in Roman format. | |||||||||||||
7. | Luna: Print current phase of Moon. |
Timings taken on SDF MetaArray IV Side B (Linux 4.19.0-17-amd64 #1 SMP Debian 4.19.194-3 (2021-07-18) x86_64 GNU/Linux). Test programs were based on program indicated in column headers with processing enclosed in a 1,000,000-times repeating loop. All programs passed the same argument(s) and all output redirected to /dev/null.
Language | Verbum x 1M | Numrom x 1M | ||
---|---|---|---|---|
immut. list4 | mut. list5 | lookup6 | ||
C | 0.030s | 0.247s | - | 0.048s |
Pascal | 0.100s | 0.463s | - | 0.208s |
Common Lisp7 | 0.556s | 2.480s | 2.455s | 0.834 |
Perl | 0.114s | 9.571s | 8.831s | 0.566s |
Rexx (rexx1) | 0.448s | 24.506s | 15.453s | 3m59.605s |
Rexx (Regina) | 0.463s | 23.657s | 17.366s | 3m12.283s |
Ruby | 0.519s | 5.280s | 5.331s | 0.596s |
Python | 0.574s | 11.803s | 13.030s | 2.937s |
TCL2 | 1.601s | 1.504s | ||
Bourne shell | 3.239s | |||
Bash3 | 5.397s |
Timings taken on lucy (Linux 5.10.0-21-amd64 #1 SMP Debian 5.10.162-1 (2023-01-21) x86_64 GNU/Linux Intel Atom N475 CPU). Test programs were based on program indicated in column headers with processing enclosed in a 1,000,000-times repeating loop. All programs passed the same argument(s) and all output redirected to /dev/null.
C programs compiled with gcc 10.2.1 (10.2.1-6 20210110 Debian) on lucy.
C# programs compiled with mcs Mono C# compiler and executed with Mono 6.8.0.105 (6.8.0.105+dfsg-3.3~deb11u1 Debian) on lucy .
Language | Verbum x 1M | Numrom x 1M | |
---|---|---|---|
immut. list4 | lookup6 | ||
C | 0.175s | 1.306s | 0.383s |
C# | 8.787s | 29.092s | 13.320s |
Timings taken on boxy (Windows 7 Professional SP1 Intel Core i7-3770 CPU). Test programs were based on program indicated in column headers with processing enclosed in a 1,000,000-times repeating loop. All programs passed the same argument(s) and all output redirected to $null.
Language | Compiler | Verbum x 1M | Numrom x 1M | ||
---|---|---|---|---|---|
immut. list4 | lookup6 | ||||
C | Visual Studio 2019 | 3.371s | 3.975s | 3.834s | |
gcc 11.2.0 (MSYS2) | 5.674s | 5.284s | 5.163s | ||
C# | Visual Studio 2019 | 3.912s | 4.009s | 4.112s | |
mcs 6.8.0.105 (lucy) | 6.321s | 5.695s | 5.901s |
Timings taken on slabby (Windows 10 Home 22H2 Intel Atom x5-Z8550 CPU). Test programs were based on program indicated in column headers with processing enclosed in a 1,000,000-times repeating loop. All programs passed the same argument(s) and all output redirected to $null.
Language | Compiler | Verbum x 1M | Numrom x 1M | ||
---|---|---|---|---|---|
immut. list4 | lookup6 | ||||
C | Visual Studio 2019 (boxy) | 2.789s | 2.331s | 2.295s | |
C# | Visual Studio 2019 (boxy) | 23.852s | 27.217s | 25.172s |
1 Static version of Regina.
2 Controls access to /dev/null internally instead of shell redirection.
3 Uses Bash-specific range operator. Running standard Bourne shell script with Bash takes 11.608s.
4 Immutable list: List of Roman numeral symbols and values processed with index variable.
5 Mutable list: List of Roman numeral symbols processed by discarding members as used.
6 Lookup: Roman numeral looked-up in array of all 5000 possible numerals.
7 Run with Steel Bank Common Lisp on MetaArray (4.19.0-22-amd64 #1 SMP Debian 4.19.260-1 (2022-09-29) x86_64 GNU/Linux).