Letter frequency: Difference between revisions
(added php) |
(added objective-c) |
||
Line 60: | Line 60: | ||
<lang j> ltrfreq 'config.file' |
<lang j> ltrfreq 'config.file' |
||
88 17 17 24 79 18 19 19 66 0 2 26 26 57 54 31 1 53 43 59 19 6 2 0 8 0</lang> |
88 17 17 24 79 18 19 19 66 0 2 26 26 57 54 31 1 53 43 59 19 6 2 0 8 0</lang> |
||
=={{header|Objective-C}}== |
|||
<lang objc>#import <Foundation/Foundation.h> |
|||
int main (int argc, const char *argv[]) { |
|||
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; |
|||
NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding]]; |
|||
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; |
|||
NSCountedSet *countedSet = [[NSCountedSet alloc] init]; |
|||
NSUInteger len = [string length]; |
|||
for (NSUInteger i = 0; i < len; i++) { |
|||
unichar c = [string characterAtIndex:i]; |
|||
if ([[NSCharacterSet letterCharacterSet] characterIsMember:c]) |
|||
[countedSet addObject:[NSNumber numberWithInteger:c]]; |
|||
} |
|||
[string release]; |
|||
for (NSNumber *chr in countedSet) { |
|||
NSLog(@"%C => %lu", (unichar)[chr integerValue], [countedSet countForObject:chr]); |
|||
} |
|||
[countedSet release]; |
|||
[pool release]; |
|||
return 0; |
|||
}</lang> |
|||
=={{header|OCaml}}== |
=={{header|OCaml}}== |
Revision as of 05:08, 22 September 2011
Open a text file and compute letter frequency.
The first 5 solutions (Aikido, C, J, Python, SIMPOL) moved here from the Array page. They might not all accomplish the same task.
Aikido
<lang aikido>import ctype
var letters = new int [26]
var s = openin (args[0]) while (!s.eof()) {
var ch = s.getchar() if (s.eof()) { break } if (ctype.isalpha (ch)) { var n = cast<int>(ctype.tolower(ch) - 'a') ++letters[n] }
}
foreach i letters.size() {
println (cast<char>('a' + i) + " " + letters[i])
}</lang>
C
<lang c>/* declare array */ int frequency[26]; int ch; FILE* txt_file = fopen ("a_text_file.txt", "rt");
/* init the freq table: */ for (ch = 0; ch < 26; ch++)
frequency[ch] = 0;
while (1) {
ch = fgetc(txt_file); if (ch == EOF) break; /* end of file or read error. EOF is typically -1 */
/* assuming ASCII; "letters" means "a to z" */ if ('a' <= ch && ch <= 'z') /* lower case */ frequency[ch-'a']++; else if ('A' <= ch && ch <= 'Z') /* upper case */ frequency[ch-'A']++;
}</lang>
J
The example task is the same: open a text file and compute letter frequency.
Input is a directory-path with filename. Result is 26 integers.
<lang j>require 'files' NB. define fread ltrfreq=: 3 : 0
letters=. u: 65+i.26 NB. upper case letters <: #/.~ (toupper fread y) (,~ -. -.) letters
)</lang>
Example use (based on a configuration file from another task):
<lang j> ltrfreq 'config.file' 88 17 17 24 79 18 19 19 66 0 2 26 26 57 54 31 1 53 43 59 19 6 2 0 8 0</lang>
Objective-C
<lang objc>#import <Foundation/Foundation.h>
int main (int argc, const char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding]]; NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSCountedSet *countedSet = [[NSCountedSet alloc] init]; NSUInteger len = [string length]; for (NSUInteger i = 0; i < len; i++) { unichar c = [string characterAtIndex:i]; if ([[NSCharacterSet letterCharacterSet] characterIsMember:c]) [countedSet addObject:[NSNumber numberWithInteger:c]]; } [string release]; for (NSNumber *chr in countedSet) { NSLog(@"%C => %lu", (unichar)[chr integerValue], [countedSet countForObject:chr]); } [countedSet release]; [pool release]; return 0;
}</lang>
OCaml
We open a text file and compute letter frequency. Other characters than [a-z] and [A-Z] are ignored, and upper case letters are first converted to lower case before to compute letter frequency.
<lang ocaml>let () =
let ic = open_in Sys.argv.(1) in let base = int_of_char 'a' in let arr = Array.make 26 0 in try while true do let c = Char.lowercase(input_char ic) in let ndx = int_of_char c - base in if ndx < 26 && ndx >= 0 then arr.(ndx) <- succ arr.(ndx) done with End_of_file -> close_in ic; for i=0 to 25 do Printf.printf "%c -> %d\n" (char_of_int(i + base)) arr.(i) done</lang>
Perl
Counts letters in files given on command line or piped to stdin. Case insensitive. <lang perl>while (<>) { $cnt{lc chop}++ while length } print "$_: ", $cnt{$_}//0, "\n" for 'a' .. 'z';</lang>
Perl 6
<lang perl6>(my %count){$_}++ for lines.comb; .say for %count.sort;</lang> The lines function automatically opens the file supplied on the command line. This program does not count newlines.
The following should work by spec, but nobody implements the Bag type yet: <lang perl6>.say for slurp.comb.Bag.pairs.sort;</lang>
PHP
<lang php><?php print_r(array_count_values(str_split(file_get_contents($argv[1])))); ?></lang>
Python
Using collections.Counter
<lang python>import collections, sys
def filecharcount(openfile):
c = collections.Counter() for line in openfile: c.update(line) return sorted(c.items())
f = open(sys.argv[1]) print(filecharcount(f))</lang>
Not using collections.Counter
<lang python>import string if hasattr(string, ascii_lowercase):
letters = string.ascii_lowercase # Python 2.2 and later
else:
letters = string.lowercase # Earlier versions
offset = ord('a')
def countletters(file_handle):
"""Traverse a file and compute the number of occurences of each letter """return results as a simple 26 element list of integers. results = [0] * len(letters) for line in file_handle: for char in line: char = char.lower() if char in letters: results[offset - ord(char)] += 1 # Ordinal of 'a' minus ordinal of any lowercase ASCII letter -> 0..25 return results
if __name__ == "__main__":
sourcedata = open(sys.argv[1]) lettercounts = countletters(sourcedata) for i in xrange(len(lettercounts)): print "%s=%d" % (chr(i + ord('a')), lettercounts[i]),</lang>
This example defines the function and provides a sample usage. The if ... __main__... line allows it to be cleanly imported into any other Python code while also allowing it to function as a standalone script. (A very common Python idiom).
Using a numerically indexed array (list) for this is artificial and clutters the code somewhat.
Using defaultdict
<lang python>... from collections import defaultdict def countletters(file_handle):
"""Count occurences of letters and return a dictionary of them """ results = defaultdict(int) for line in file_handle: for char in line: if char.lower() in letters: c = char.lower() results[c] += 1 return results</lang>
Which eliminates the ungainly fiddling with ordinal values and offsets in function countletters of a previous example above. More importantly it allows the results to be more simply printed using:
<lang python>lettercounts = countletters(sourcedata) for letter,count in lettercounts.iteritems():
print "%s=%s" % (letter, count),</lang>
Again eliminating all fussing with the details of converting letters into list indices.
SIMPOL
Example: open a text file and compute letter frequency. <lang simpol>constant iBUFSIZE 500
function main(string filename)
fsfileinputstream fpi integer e, i, aval, zval, cval string s, buf, c array chars
e = 0 fpi =@ fsfileinputstream.new(filename, error=e) if fpi =@= .nul s = "Error, file """ + filename + """ not found{d}{a}" else chars =@ array.new() aval = .charval("a") zval = .charval("z") i = 1 while i <= 26 chars[i] = 0 i = i + 1 end while buf = .lcase(fpi.getstring(iBUFSIZE, 1)) while not fpi.endofdata and buf > "" i = 1 while i <= .len(buf) c = .substr(buf, i, 1) cval = .charval(c) if cval >= aval and cval <= zval chars[cval - aval + 1] = chars[cval - aval + 1] + 1 end if i = i + 1 end while buf = .lcase(fpi.getstring(iBUFSIZE, 1)) end while
s = "Character counts for """ + filename + """{d}{a}" i = 1 while i <= chars.count() s = s + .char(aval + i - 1) + ": " + .tostr(chars[i], 10) + "{d}{a}" i = i + 1 end while end if
end function s</lang>
As this was being created I realized that in [SIMPOL] I wouldn't have done it this way (in fact, I wrote it differently the first time and had to go back and change it to use an array afterward). In [SIMPOL] we would have used the set object. It acts similarly to a single-dimensional array, but can also use various set operations, such as difference, unite, intersect, etc. One of th einteresting things is that each unique value is stored only once, and the number of duplicates is stored with it. The sample then looks a little cleaner:
<lang simpol>constant iBUFSIZE 500
function main(string filename)
fsfileinputstream fpi integer e, i, aval, zval string s, buf, c set chars
e = 0 fpi =@ fsfileinputstream.new(filename, error=e) if fpi =@= .nul s = "Error, file """ + filename + """ not found{d}{a}" else chars =@ set.new() aval = .charval("a") zval = .charval("z") buf = .lcase(fpi.getstring(iBUFSIZE, 1)) while not fpi.endofdata and buf > "" i = 1 while i <= .len(buf) c = .substr(buf, i, 1) if .charval(c) >= aval and .charval(c) <= zval chars.addvalue(c) end if i = i + 1 end while buf = .lcase(fpi.getstring(iBUFSIZE, 1)) end while
s = "Character counts for """ + filename + """{d}{a}" i = 1 while i <= chars.count() s = s + chars[i] + ": " + .tostr(chars.valuecount(chars[i]), 10) + "{d}{a}" i = i + 1 end while end if
end function s</lang>
The final stage simply reads the totals for each character. One caveat, if a character is unrepresented, then it will not show up at all in this second implementation.
Tcl
<lang tcl>proc letterHistogram {fileName} {
# Initialize table (in case of short texts without every letter) for {set i 97} {$i<=122} {incr i} { set frequency([format %c $i]) 0 } # Iterate over characters in file set f [open $fileName] foreach c [split [read $f] ""] { # Count them if they're alphabetic if {[string is alpha $c]} { incr frequency([string tolower $c]) } } close $f # Print the histogram parray frequency
}
letterHistogram the/sample.txt</lang>
Vedit macro language
<lang vedit>File_Open("c:\txt\a_text_file.txt") Update()
for (#1='A'; #1<='Z'; #1++) {
Out_Reg(103) Char_Dump(#1,NOCR) Out_Reg(CLEAR) #2 = Search(@103, BEGIN+ALL+NOERR) Message(@103) Num_Type(#2)
}</lang>
Example output:
A 76 B 23 C 51 D 64 E 192 F 51 G 32 H 59 I 146 J 1 K 9 L 73 M 34 N 94 O 113 P 27 Q 1 R 92 S 89 T 138 U 63 V 26 W 35 X 16 Y 16 Z 2