D deserves much more popularity. It's much better language than Go, with many similiar strenghts. If only it had Google's backing... Eh...
I still prefer Rust for many reasons (no exceptions, no GC, memory safety), but in my heart D is the second favourite - the more lightweight, expressive and pragmatic. I especially wish Rust had macro system and compilation times of D because they are the best in the class. :]
Without looking at any other solutions, I whipped up a one-off 37-line C++11 program that is easily twice as fast as the best D or Rust one, with no special tricks. (Change out the uses of "auto" and it qualifies as C++98.)
I am appalled that a 125-line program to do such a simple job could win. If that was the best they could do, I would have kept the challenge open until something better came in.
I haven't tried the other programs on one-field files, or where the field selected is the first, or the last. Did you?
#include <iostream>
#include <fstream>
#include <streambuf>
#include <algorithm>
#include <string>
int main(int, char** argv) {
std::ifstream in(argv[1]);
std::string head;
if (!(in >> head)) return std::cout << "input file missing\n", 1;
head = "," + head + ",";
auto sub = std::string(argv[3]);
auto target = "," + std::string(argv[2]) + ",";
auto pos = head.find(target);
if (pos == std::string::npos)
return std::cout << "column name doesn’t exists in the input file\n", 1;
auto skip_pre = pos ? std::count(&head[0], &head[pos], ',') : 0;
auto skip_post = std::count(
&head[pos + target.size()], &head[head.size()], ',');
std::ofstream out(argv[4]);
auto ib = in.rdbuf(), ob = out.rdbuf();
ob->sputn(&head[1], head.size()-2); ob->sputc('\n');
bool skipping = pos == 0;
for (int c = 0, count = skip_pre; (c = ib->snextc()) != EOF; ) {
if (c == ',' || c == '\n') {
if (skipping) {
count = skip_post + skip_pre;
skipping = count == 0;
ob->sputn(&sub[0], sub.size());
} else if (--count == 0) {
skipping = true;
}
ob->sputc(c);
} else if (!skipping)
ob->sputc(c);
}
}
> I am appalled that a 125-line program to do such a simple job could win.
The goal of that challenge was to pack the most C++17 features in a program, and it was just a challenge organized by a couple of blogs.
The winner was also a self-confessed amateur. That's a telltale sign of how many people have participated, and the level of engagement that the challenge had.
$ txr solve.tl < data
name,surname,city,country
Adam,Jones,Manchester,UK
Joe,Doe,Cracow,Poland
Michael,Smith,Paris,France
Alex,McNeil,Gdynia,Poland
With args:
$ txr solve.tl city London < data
name,surname,city,country
Adam,Jones,London,UK
Joe,Doe,London,Poland
Michael,Smith,London,France
Alex,McNeil,London,Poland
We need (:inputs stdin) so that awk takes its input from standard input rather than processing the city London arguments as input sources.
open Core
let () =
if Array.length Sys.argv <> 5 then
Printf.eprintf "Invalid args\nUsage: %s <input.csv> <column-name> <replacement-string> <output.csv>" Sys.argv.(0)
else
let input_file, column_name, replacement, output_file = Sys.argv.(1), Sys.argv.(2),Sys.argv.(3),Sys.argv.(4) in
In_channel.with_file input_file ~f:(fun in_ch ->
match In_channel.input_line in_ch with
| None -> Printf.eprintf "Cannot read first line"
| Some first_line ->
let columns = String.split first_line ~on:',' in
match List.findi columns ~f:(fun _ col -> col = column_name) with
| None -> Printf.eprintf "Cannot find column: %s" column_name
| Some (index, _) ->
let _ = Out_channel.with_file output_file ~f:(fun out_ch ->
Out_channel.output_string out_ch (first_line ^ "\n");
In_channel.fold_lines in_ch ~init:() ~f:(fun _ line ->
let in_columns = String.split line ~on:',' in
let out_columns = List.mapi ~f:(fun i col -> if i = index then replacement else col) in_columns in
let out_line = (String.concat ~sep:"," out_columns) ^ "\n" in
Out_channel.output_string out_ch out_line
)
) in ())
I don't agree. The comparisons are rather disingenuous as the representative examples hand-picked for the other languages are rather crude and too much verbose. For instance, the 120-loc C++ program needlessly introduces functions that run a single loc (by adding 4 or 5 loc to the bill) and add a dozen or so lines by breaking long lines.
...and by "most expressive C++17 [program]" it actually meant this:
> So the purpose of this challenge is to write a piece of code that contains as many features of C++17 as possible, and that is as clear as possible.
...which, clearly, states that the goal was not minimize lines of code.
I bet D fanboys can find more apples-to-oranges comparisons where D comes out as the most citric alternative when compared with apples and bananas.
And by the way, the contest was organized by a couple of blogs, and the winner was a self-confessed amateur. Hardly a tour de force in C++ programming.
This is a very well written article. The author shows an idiomatic D code and explains the details line by line. Clearly emphasizes the expressiveness of D.
I still prefer Rust for many reasons (no exceptions, no GC, memory safety), but in my heart D is the second favourite - the more lightweight, expressive and pragmatic. I especially wish Rust had macro system and compilation times of D because they are the best in the class. :]