-
Notifications
You must be signed in to change notification settings - Fork 34
/
huffman_coding.pl
72 lines (55 loc) · 1.68 KB
/
huffman_coding.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/perl
# https://rosettacode.org/wiki/Huffman_coding#Perl
use 5.020;
use strict;
use warnings;
use experimental qw(signatures);
# produce encode and decode dictionary from a tree
sub walk ($node, $code, $h, $rev_h) {
my $c = $node->[0];
if (ref $c) { walk($c->[$_], $code . $_, $h, $rev_h) for (0, 1) }
else { $h->{$c} = $code; $rev_h->{$code} = $c }
return ($h, $rev_h);
}
# make a tree, and return resulting dictionaries
sub mktree ($bytes) {
my (%freq, @nodes);
++$freq{$_} for @$bytes;
@nodes = map { [$_, $freq{$_}] } sort { $a <=> $b } keys %freq;
do { # poor man's priority queue
@nodes = sort { $a->[1] <=> $b->[1] } @nodes;
my ($x, $y) = splice(@nodes, 0, 2);
if (defined($x)) {
if (defined($y)) {
push @nodes, [[$x, $y], $x->[1] + $y->[1]];
}
else {
push @nodes, [[$x], $x->[1]];
}
}
} while (@nodes > 1);
walk($nodes[0], '', {}, {});
}
sub encode ($bytes, $dict) {
join('', map { $dict->{$_} // die("bad char $_") } @$bytes);
}
sub decode ($str, $dict) {
my ($seg, @out) = ("");
# append to current segment until it's in the dictionary
foreach my $bit (split('', $str)) {
$seg .= $bit;
my $x = $dict->{$seg} // next;
push @out, $x;
$seg = '';
}
die "bad code" if length($seg);
return \@out;
}
my $txt = 'this is an example for huffman encoding';
my @bytes = unpack('C*', $txt);
my ($h, $rev_h) = mktree(\@bytes);
for (keys %$h) { printf("%3d: %s\n", $_, $h->{$_}) }
my $enc = encode(\@bytes, $h);
say $enc;
my $dec = decode($enc, $rev_h);
say pack('C*', @$dec);