Kernighans large earthquake problem
Brian Kernighan, in a lecture at the University of Nottingham, described a problem on which this task is based.
You are encouraged to solve this task according to the task description, using any language you may know.
- Problem
You are given a a data file of thousands of lines; each of three `whitespace` separated fields: a date, a one word name and the magnitude of the event.
Example lines from the file would be lines like:
8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6 3/13/2009 CostaRica 5.1
- Task
- Create a program or script invocation to find all the events with magnitude greater than 6
- Assuming an appropriate name e.g. "data.txt" for the file:
- Either: Show how your program is invoked to process a data file of that name.
- Or: Incorporate the file name into the program, (as it is assumed that the program is single use).
ALGOL 68
<lang algol68>IF FILE input file;
STRING file name = "data.txt"; open( input file, file name, stand in channel ) /= 0
THEN
# failed to open the file # print( ( "Unable to open """ + file name + """", newline ) )
ELSE
# file opened OK # BOOL at eof := FALSE; # set the EOF handler for the file # on logical file end( input file, ( REF FILE f )BOOL: BEGIN # note that we reached EOF on the latest read # at eof := TRUE; # return TRUE so processing can continue # TRUE END ); # return the real value of the specified field on the line # PROC real field = ( STRING line, INT field )REAL: BEGIN REAL result := 0; INT c pos := LWB line; INT max pos := UPB line; STRING f := ""; FOR f ield number TO field WHILE c pos <= max pos DO # skip leading spaces # WHILE IF c pos > max pos THEN FALSE ELSE line[ c pos ] = " " FI DO c pos +:= 1 OD; IF c pos <= max pos THEN # have a field # INT start pos = c pos; WHILE IF c pos > max pos THEN FALSE ELSE line[ c pos ] /= " " FI DO c pos +:= 1 OD; IF field number = field THEN # have the required field # f := line[ start pos : c pos - 1 ] FI FI OD; IF f /= "" THEN # have the field - assume it a real value and convert it # FILE real value; associate( real value, f ); on value error( real value , ( REF FILE f )BOOL: BEGIN # "handle" invalid data # result := 0; # return TRUE so processing can continue # TRUE END ); get( real value, ( result ) ) FI; result END # real field # ; # show the lines where the third field is > 6 # WHILE NOT at eof DO STRING line; get( input file, ( line, newline ) ); IF real field( line, 3 ) > 6 THEN print( ( line, newline ) ) FI OD; # close the file # close( input file )
FI</lang>
AWK
<lang awk> awk '$3 > 6' data.txt</lang>
C++
<lang c++>// Randizo was here!
- include <iostream>
- include <fstream>
- include <string>
using namespace std;
int main() {
ifstream file("../include/earthquake.txt");
int count_quake = 0; int column = 1; string value; double size_quake; string row = "";
while(file >> value) { if(column == 3) { size_quake = stod(value);
if(size_quake>6.0) { count_quake++; row += value + "\t"; cout << row << endl; }
column = 1; row = ""; } else { column++; row+=value + "\t"; } }
cout << "\nNumber of quakes greater than 6 is " << count_quake << endl;
return 0;
} </lang>
C
<lang c>#include <stdio.h>
- include <string.h>
- include <stdlib.h>
int main() {
FILE *fp; char *line = NULL; size_t len = 0; ssize_t read; char *lw, *lt; fp = fopen("data.txt", "r"); if (fp == NULL) { printf("Unable to open file\n"); exit(1); } printf("Those earthquakes with a magnitude > 6.0 are:\n\n"); while ((read = getline(&line, &len, fp)) != EOF) { if (read < 2) continue; /* ignore blank lines */ lw = strrchr(line, ' '); /* look for last space */ lt = strrchr(line, '\t'); /* look for last tab */ if (!lw && !lt) continue; /* ignore lines with no whitespace */ if (lt > lw) lw = lt; /* lw points to last space or tab */ if (atof(lw + 1) > 6.0) printf("%s", line); } fclose(fp); if (line) free(line); return 0;
}</lang>
- Output:
Using the given file:
Those earthquakes with a magnitude > 6.0 are: 8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6
Cixl
<lang cixl> use: cx;
'data.txt' `r fopen lines {
let: (time place mag) @@s split ..; let: (m1 m2) $mag @. split &int map ..; $m1 6 >= $m2 0 > and {[$time @@s $place @@s $mag] say} if
} for </lang>
- Output:
8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6
Go
<lang go>package main
import (
"bufio" "fmt" "os" "strconv" "strings"
)
func main() {
f, err := os.Open("data.txt") if err != nil { fmt.Println("Unable to open the file") return } defer f.Close() fmt.Println("Those earthquakes with a magnitude > 6.0 are:\n") input := bufio.NewScanner(f) for input.Scan() { line := input.Text() fields := strings.Fields(line) mag, err := strconv.ParseFloat(fields[2], 64) if err != nil { fmt.Println("Unable to parse magnitude of an earthquake") return } if mag > 6.0 { fmt.Println(line) } }
}</lang>
- Output:
Those earthquakes with a magnitude > 6.0 are: 8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6
Kotlin
<lang scala>// Version 1.2.40
import java.io.File
fun main(args: Array<String>) {
val r = Regex("""\s+""") println("Those earthquakes with a magnitude > 6.0 are:\n") File("data.txt").forEachLine { if (it.split(r)[2].toDouble() > 6.0) println(it) }
}</lang>
- Output:
Using the given file:
Those earthquakes with a magnitude > 6.0 are: 8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6
Lua
For each line, the Lua pattern "%S+$" is used to capture between the final space character and the end of the line. <lang lua>-- arg[1] is the first argument provided at the command line for line in io.lines(arg[1] or "data.txt") do -- use data.txt if arg[1] is nil
magnitude = line:match("%S+$") if tonumber(magnitude) > 6 then print(line) end
end</lang>
Perl
<lang perl>perl -n -e '/(\S+)\s*$/ and $1 > 6 and print' data.txt</lang>
Perl 6
Pass in a file name, or use default for demonstration purposes. <lang perl6>$_ = @*ARGS[0] ?? @*ARGS[0].IO !! q:to/END/;
8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6 3/13/2009 CostaRica 5.1 END
map { .say if .words[2] > 6 }, .lines;</lang>
Python
Typed into a bash shell or similar: <lang python>python -c ' with open("data.txt") as f:
for ln in f: if float(ln.strip().split()[2]) > 6: print(ln.strip())'</lang>
Ring
<lang ring>
- Project : Kernighans large earthquake problem
- Date : 2018/06/15
- Author : Gal Zsolt [~ CalmoSoft ~]
- Email : <calmosoft@gmail.com>
load "stdlib.ring" nr = 0 equake = list(3) revarr = list(3) fn = "equake.txt" fp = fopen(fn,"r")
while not feof(fp)
nr = nr + 1 equake[nr] = readline(fp)
end fclose(fp) for n = 1 to len(equake)
revarr[n] = revstr(equake[n])
next for n = 1 to len(revarr)
sp = substr(revarr[n]," ") sptemp = substr(revarr[n],1,sp-1) sptemp = revstr(sptemp) sptemp = number(sptemp) if sptemp > 6 see equake[n] + nl ok
next
func getFileSize(fp)
c_filestart = 0 c_fileend = 2 fseek(fp,0,c_fileend) nfilesize = ftell(fp) fseek(fp,0,c_filestart) return nfilesize
func revstr(cstr)
cStr2 = "" for x = len(cstr) to 1 step -1 cstr2 = cstr2 + cstr[x] next return cStr2
</lang> Output:
8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6
Ruby
ruby -nae "$F[2].to_f > 6 && print" data.txt
A more interesting problem. Print only the events whose magnitude is above average.
Contents of the file:
8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6 3/13/2009 CostaRica 5.1 2000-02-02 Foo 7.7 1959-08-08 Bar 6.2 1849-09-09 Pym 9.0
The command:
ruby -e"m=$<.to_a;f=->s{s.split[2].to_f};a=m.reduce(0){|t,s|t+f[s]}/m.size;puts m.select{|s|f[s]>a}" e.txt
Output:
8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6 2000-02-02 Foo 7.7 1849-09-09 Pym 9.0
Scala
<lang Scala>object Equakes extends App {
val HeavyMag = 6.0 val regex = """^.*?((?:[0-9]{1,4}[.\-\/]){2}[0-9]{2,4})(?:\s*)([\sa-zA-Z]+)((?:0|(?:[1-9][0-9]*))(?:\.[0-9]+))?.*?$""".r
val heavyOnes: Seq[String] = io.Source.fromFile("equake.txt").getLines().map { case s@regex(_, _, mag) if mag.toDouble > HeavyMag => s case _ => "" }.filter(_.nonEmpty).toSeq
println(s"Events with a magnitude greater than $HeavyMag are:\n") heavyOnes.foreach(println(_)) println(s"End of list of ${heavyOnes.length} events.")
}</lang>
- Output:
Events with a magnitude greater than 6.0 are:8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6
End of list of 2 events.
Tcl
Inspired by awk. <lang tcl>catch {console show} ;## show console when running from tclwish catch {wm withdraw .}
set filename "data.txt" set fh [open $filename] set NR 0 ;# number-of-record, means linenumber
while {[gets $fh line]>=0} { ;# gets returns length of line, -1 means eof
incr NR set line2 [regexp -all -inline {\S+} $line] ;# reduce multiple whitespace set fld [split $line2] ;# split line into fields, at whitespace set f3 [lindex $fld 2] ;# zero-based #set NF [llength $fld] ;# number-of-fields
if {$f3 > 6} { puts "$line" }
} close $fh </lang>
zkl
While lexical comparsions [of numeric data] are fine for this problem, it is bad practice so I don't do it (written so text is automatically converted to float). <lang zkl>fcn equake(data,out=Console){
data.pump(out,fcn(line){ 6.0line.split()[-1] },Void.Filter)
}</lang> <lang zkl>equake(Data(Void,
- <<<
"8/27/1883 Krakatoa 8.8\n" "5/18/1980 MountStHelens 7.6\n" "3/13/2009 CostaRica 5.1\n"
- <<<
));</lang> or <lang zkl>equake(File("equake.txt"));</lang> or <lang zkl>$ zkl --eval 'File.stdin.pump(Console,fcn(line){ 6.0<line.split()[-1] },Void.Filter)' < equake.txt</lang>
- Output:
8/27/1883 Krakatoa 8.8 5/18/1980 MountStHelens 7.6