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);
        }
    }
}

Published by Avdi Grimm

18 Comments

  1. My original elixir version was revised by José Valim, my version sucked.

    Reply
  2. what? no php?

    Reply
  3. Note that you can do without the .readlines() in the Python version, leaving:

    open(sys.argv[2], ‘w’).writelines(sorted(open(sys.argv[1])))

    Reply
  4. Here’s my golf submission for Haskell 🙂

    https://gist.github.com/MichaelXavier/7015022

    Reply
  5. File.write has been introduced in 1.9.3

    Reply
  6. Not idiomatic Smalltalk but my attempt:

    inputFileName := Smalltalk arguments first.

    outputFileName := Smalltalk arguments second.

    FileStream readOnlyFileNamed: inputFileName

    do: [:inputStream | FileStream newFileNamed: outputFileName
    
      do: [:outputStream | inputStream contents lines sort
    
        do: [:each | outputStream nextPutAll: each] separatedBy: [outputStream crlf]]]
    
    Reply
  7. 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

    Reply
  8. Fun post! Here’s an example in Groovy:

    https://gist.github.com/codetojoy/7027625

    Reply
  9. My friend was complaining about perl being symbol heavy. Opposite:

    use File::Slurp;
    write_file pop(@ARGV), sort(read_file(@ARGV));
    
    Reply
  10. 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

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *