simon-hofmann.org

Welcome to my mind.
Watch your step, it's a bit messy around here!

D vs. Go vs. Python

• D,Go,Python

Leeeeet’s get reeeeeeaaaady tooooooo ruuuuuuumble!

I got interested in D after I had to write some code for a university class. I really liked its syntax and features and so I decided to pay a bit more attention to it. After finding out about D’s huge standard library and the DUB build system I got even more interested.

I began to re-implement some of my Python tools, which I needed for my university projects, in D. ! surprisingly easy and fun task! People often say they prefer Python because of its possibility to get things done fast, but after I ported some of my tools I realized that this is also possible using D!

Enough talking, what is this post all about?

While strolling StackOverflow today I stumbled upon a question which asked how to estimate a file’s word count, similar to

wc -w $file.txt

After reading the answer I knew that in D this can be done in a single line. Given the solution I was amazed by D’s standard library, but on the other hand also curious how easy and in how many lines of Python this task could be done. And since I wanted to try another language, I chose to also see how easy this was using Go.

So the following three snippets all perform the same basic task:

Read a file, split it into lines, split lines into words and count each word (skipping whitespaces, newlines etc.).

wc -w in Python

Iterating over a the lines of our source file is no big problem in Python:

from sys import argv

with open(argv[0], 'r') as f:
	for line in f:
	    print(line)

Splitting a line into words is already built in, so counting a line’s words is straight forward:

from sys import argv

with open(argv[0], 'r') as f:
	count = 0
	for line in f:
	    count += len(line.split())
	print(count)

wc -w in Go

For me the Python implementation was straight forward. With Go this was a different story. I’ve never written a single line of Go before this post, so I started from 0.

With a little help from gobyexample I ran a hello world, and after a little bit of reading in the official Go docs and some more examples I had my wc -w implementation.

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	f, err := os.Open(os.Args[1])
	if err != nil {
	    panic(err)
	}

	defer f.Close()

	fileScanner := bufio.NewScanner(f)
	fileScanner.Split(bufio.ScanWords)
	var count int

	for fileScanner.Scan() {
	    count++
	}
	fmt.Println(count)
}

Since this is my first piece of Go code I doubt that this implementation is perfect (If you’ve any hints, please let me know!), but compared with the Python implementation it is far less intuitive.

That’s it for Python and Go, let’s take a look at D!

wc -w in D

int main(string[] args) {
	import std.stdio: writeln;
	import std.file: readText, FileException;
	import std.algorithm: splitter;
	import std.range.primitives: walkLength;
	import std.utf: UTFException;

	try {
		readText(args[1]).splitter.walkLength().writeln();
	} catch(FileException e) {
		writeln(e.msg);
	        return -1;
	} catch(UTFException e) {
		writeln(e.msg);
		return -1;
	}

	return 0;
}

Quite a lot of local imports and a bit of error handling, but the thing the got me is the single line implementation:

    writeln(readText(args[1]).splitter.walkLength());

No loops or anything, the program logic is hidden in three function calls, nicely chained using D’s Uniform Function Call Syntax (UFCS).

    readText

reads the contents of a file into a string,

    splitter

splits the string into an InputRange and

    walkLength

estimates the Range’s length. All in one line and once you’re familiar with D’s UFCS pretty straight forward!

That’s it for now, but I’ll keep going in D!

So long

Simon