Jussi Pakkanen writes that C++ has become a scripting language. As evidence, he presents the following 19-line program in idiomatic C++11 which reads lines from the first command-line argument, sorts the lines, and writes them to the second argument.
#include<string> #include<vector> #include<algorithm> #include<fstream> using namespace std; int main(int argc, char **argv) { ifstream ifile(argv[1]); ofstream ofile(argv[2]); string line; vector<string> data; while(getline(ifile, line)) { data.push_back(line); } sort(data.begin(), data.end()); for(const auto &i : data) { ofile << i << std::endl; } return 0; }
I’m an incorrigible fan of C++ from days of old; I still prefer it to straight C for most low-level systems programming tasks. And I have to admit that this is admirably concise for a C++ program.
However, I thought it might be fun to see what this program looks like in other “scripting” languages. I used a file containing the following lines to test my implementations.
D is for Durian B is for Breadfruit C is for Clementine A is for Apple
Starting with Ruby, since it’s what I spend most of my time in these days (this example has been updated with feedback from Joel VanderWerf):
File.write(ARGV.pop, ARGF.sort.join)
This example is notable for using File.write , introduced in either 1.9.3 or 2.0, I can’t remember. It also uses ARGF to refer to “the concatenation of all files supplied on the command line”. At the point this is evaluated, the output file has already been pop‘ed off of the ARGV array.
Next, a Perl version.
open OUT, ">", pop(@ARGV); print OUT sort(<>);
This being Perl, and my Perl being incredibly rusty, I’m sure it could be golfed down to a one-liner by a pro. In this code, the empty line-reading operator (<>) serves the same purpose as ARGF.each_line above.
Now, how about an example using the grandparent of all scripting languages, TCL.
set in [open [lindex $argv 0]] set out [open [lindex $argv 1] w] while {[gets $in line] > 0} { lappend lines $line } set lines [lsort $lines] foreach line $lines { puts $out $line }
As you can see, TCL does not put the same premium on succinctness as some of the later scripting languages. Still, 9 lines ain’t bad.
Of course no comparison of scripting languages would be complete without an example in Bourne Shell:
cat $1 | sort > $2
EDIT: Stephen Ball points out that this is a superfluous use of cat. It should be:
sort $1 > $2
To sum up, the C++ version is twice as long as the longest of my scripting language implementations. So while C++11 has made advances, I don’t think I’ll be using it for everyday scripting tasks anytime soon.
Thank you to Jussi Pakkanen for giving me an excuse to dust off my Perl and TCL skills. Writing these was an entertaining diversion.
UPDATE: Here are some more versions people have kindly contributed.
A Go version, from Bill Mill:
package main import ( "io/ioutil"; "os"; "strings"; "sort" ) func main() { content, _ := ioutil.ReadFile(os.Args[1]) lines := strings.Split(strings.TrimSpace(string(content)), "\n") sort.Strings(lines) ioutil.WriteFile(os.Args[2], []byte(strings.Join(lines, "\n")), 0666) }
A Python version, also from Bill Mill, and incorporating feedback from commenter Keith:
import sys open(sys.argv[2], 'w').writelines(sorted(open(sys.argv[1])))
Here it is in Elixir, from Eduardo Krustofski:
[input, output] = System.argv File.write!(output, File.stream!(input) |> Enum.sort |> Enum.join(""))
And here’s Clojure, from Ricardo Mendes:
(->> (slurp (nth *command-line-args* 1)) clojure.string/split-lines sort (clojure.string/join "\n") (spit (nth *command-line-args* 2)))
A Rust version, from ajuckel:
extern mod extra; use std::*; use extra::*; fn main() { let reader = io::file_reader(&Path(os::args()[1])).unwrap(); let writer = io::file_writer(&Path(os::args()[2]), [io::Append, io::Create]).unwrap(); let mut lines = reader.read_lines(); sort::quick_sort3(lines); for l in lines.iter() { writer.write_line(*l) } }
Haskell, from Michael Xavier:
import Data.List (sort) import System.Environment (getArgs) main = do (inFile:outFile:_) <- getArgs writeFile outFile . unlines . sort . lines =<< readFile inFile
Lua, from Peter Aronoff:
io.input(arg[1]) io.open(arg[2], 'w') io.output(arg[2]) lines = {} for line in io.lines() do lines[#lines+1] = line end table.sort(lines) for _, line in ipairs(lines) do io.write(line, "\n") end
PHP, from joonty:
<?php $input = file("php://stdin"); sort($input); file_put_contents(end($argv), join($input));
Caius Durling contributed an AppleScript version, but since AppleScript has no built-in sort function it’s a bit long to include inline.
C#, from Ellyll:
sing System.IO; using System.Linq; namespace SortingExample { public class Sorter { static void Main(string[] args) { var lines = File.ReadAllLines(args[0]).OrderBy(a => a); File.WriteAllLines(args[1], lines); } } }
My original elixir version was revised by José Valim, my version sucked.
My thanks to both of you!
Enum.join(“”) in the Elixir version is superfluous.
what? no php?
There is now!
Note that you can do without the .readlines() in the Python version, leaving:
open(sys.argv[2], ‘w’).writelines(sorted(open(sys.argv[1])))
Doh, I forgot that was the default iterator. Nice golfing.
Here’s my golf submission for Haskell 🙂
https://gist.github.com/MichaelXavier/7015022
Thanks, added!
File.write has been introduced in 1.9.3
Not idiomatic Smalltalk but my attempt:
inputFileName := Smalltalk arguments first.
outputFileName := Smalltalk arguments second.
FileStream readOnlyFileNamed: inputFileName
What Smalltalk implementation should I test this with?
A Lua version. Not golfed at all, and Lua lacks many of the built-in tools of other scripting languages, but still pretty compact.
https://gist.github.com/7024577
Thank you!
Fun post! Here’s an example in Groovy:
https://gist.github.com/codetojoy/7027625
My friend was complaining about perl being symbol heavy. Opposite:
Here’s my C# version, it’s not bad but still not really upto scripting level I’d say:
https://gist.github.com/Ellyll/7716439
Thanks! I added it to the post.