Filename | /home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm |
Statements | Executed 300132 statements in 1.28s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
30000 | 1 | 1 | 1.12s | 132s | reply | Hailo::
30002 | 3 | 1 | 64.0ms | 117ms | _storage (xsub) | Hailo::
30000 | 1 | 1 | 46.3ms | 94.2ms | _tokenizer (xsub) | Hailo::
30000 | 1 | 1 | 45.8ms | 63.0ms | _engine (xsub) | Hailo::
3 | 1 | 1 | 5.67ms | 117ms | _new_class | Hailo::
1 | 1 | 1 | 2.24ms | 20.4ms | BEGIN@11 | Hailo::
1 | 1 | 1 | 937µs | 20.3ms | BEGIN@15 | Hailo::
1 | 1 | 1 | 435µs | 20.9ms | BEGIN@10 | Hailo::
1 | 1 | 1 | 259µs | 279µs | BEGIN@184 | Hailo::
3 | 3 | 1 | 166µs | 117ms | __ANON__[:176] | Hailo::
13 | 1 | 1 | 58µs | 58µs | CORE:regcomp (opcode) | Hailo::
16 | 2 | 1 | 42µs | 42µs | CORE:match (opcode) | Hailo::
1 | 1 | 1 | 37µs | 107µs | BEGIN@9 | Hailo::
1 | 1 | 1 | 26µs | 3.92s | DEMOLISH | Hailo::
3 | 1 | 1 | 19µs | 19µs | CORE:sort (opcode) | Hailo::
1 | 1 | 1 | 18µs | 3.92s | save | Hailo::
1 | 1 | 1 | 18µs | 18µs | BEGIN@2 | Hailo::
1 | 1 | 1 | 15µs | 101µs | BEGIN@14 | Hailo::
1 | 1 | 1 | 14µs | 92µs | BEGIN@17 | Hailo::
3 | 1 | 1 | 11µs | 11µs | __ANON__[:117] | Hailo::
1 | 1 | 1 | 11µs | 36µs | BEGIN@129 | Hailo::
1 | 1 | 1 | 11µs | 64µs | BEGIN@13 | Hailo::
1 | 1 | 1 | 11µs | 70µs | BEGIN@9.2 | Hailo::
3 | 2 | 1 | 10µs | 12µs | brain (xsub) | Hailo::
1 | 1 | 1 | 10µs | 768µs | BEGIN@12 | Hailo::
1 | 1 | 1 | 10µs | 18µs | __ANON__[:71] | Hailo::
3 | 1 | 1 | 5µs | 5µs | PLUGINS (xsub) | Hailo::
1 | 1 | 1 | 5µs | 5µs | BEGIN@5 | Hailo::
2 | 1 | 1 | 4µs | 4µs | tokenizer_class (xsub) | Hailo::
2 | 1 | 1 | 3µs | 3µs | order (xsub) | Hailo::
1 | 1 | 1 | 2µs | 2µs | engine_class (xsub) | Hailo::
1 | 1 | 1 | 2µs | 2µs | engine_args (xsub) | Hailo::
1 | 1 | 1 | 2µs | 2µs | save_on_exit (xsub) | Hailo::
1 | 1 | 1 | 2µs | 2µs | storage_class (xsub) | Hailo::
1 | 1 | 1 | 2µs | 2µs | tokenizer_args (xsub) | Hailo::
1 | 1 | 1 | 1µs | 1µs | storage_args (xsub) | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[:108] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[:151] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[:152] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[:157] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[:161] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[:188] | Hailo::
0 | 0 | 0 | 0s | 0s | __ANON__[:39] | Hailo::
0 | 0 | 0 | 0s | 0s | _is_interactive | Hailo::
0 | 0 | 0 | 0s | 0s | _learn_one | Hailo::
0 | 0 | 0 | 0s | 0s | _train_fh | Hailo::
0 | 0 | 0 | 0s | 0s | learn | Hailo::
0 | 0 | 0 | 0s | 0s | learn_reply | Hailo::
0 | 0 | 0 | 0s | 0s | stats | Hailo::
0 | 0 | 0 | 0s | 0s | train | Hailo::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | package Hailo; | ||||
2 | # spent 18µs within Hailo::BEGIN@2 which was called:
# once (18µs+0s) by main::BEGIN@6 at line 4 | ||||
3 | 1 | 6µs | $Hailo::AUTHORITY = 'cpan:AVAR'; | ||
4 | 1 | 24µs | 1 | 18µs | } # spent 18µs making 1 call to Hailo::BEGIN@2 |
5 | # spent 5µs within Hailo::BEGIN@5 which was called:
# once (5µs+0s) by main::BEGIN@6 at line 7 | ||||
6 | 1 | 9µs | $Hailo::VERSION = '0.57'; | ||
7 | 1 | 16µs | 1 | 5µs | } # spent 5µs making 1 call to Hailo::BEGIN@5 |
8 | |||||
9 | 4 | 65µs | 3 | 235µs | use 5.010; # spent 107µs making 1 call to Hailo::BEGIN@9
# spent 70µs making 1 call to Hailo::BEGIN@9.2
# spent 59µs making 1 call to feature::import |
10 | 2 | 165µs | 2 | 20.9ms | # spent 20.9ms (435µs+20.4) within Hailo::BEGIN@10 which was called:
# once (435µs+20.4ms) by main::BEGIN@6 at line 10 # spent 20.9ms making 1 call to Hailo::BEGIN@10
# spent 7µs making 1 call to autodie::import |
11 | 2 | 216µs | 2 | 21.1ms | # spent 20.4ms (2.24+18.1) within Hailo::BEGIN@11 which was called:
# once (2.24ms+18.1ms) by main::BEGIN@6 at line 11 # spent 20.4ms making 1 call to Hailo::BEGIN@11
# spent 725µs making 1 call to Any::Moose::import |
12 | 2 | 28µs | 2 | 1.53ms | # spent 768µs (10+759) within Hailo::BEGIN@12 which was called:
# once (10µs+759µs) by main::BEGIN@6 at line 12 # spent 768µs making 1 call to Hailo::BEGIN@12
# spent 759µs making 1 call to Any::Moose::import |
13 | 2 | 28µs | 2 | 116µs | # spent 64µs (11+53) within Hailo::BEGIN@13 which was called:
# once (11µs+53µs) by main::BEGIN@6 at line 13 # spent 64µs making 1 call to Hailo::BEGIN@13
# spent 53µs making 1 call to Exporter::import |
14 | 2 | 33µs | 2 | 186µs | # spent 101µs (15+85) within Hailo::BEGIN@14 which was called:
# once (15µs+85µs) by main::BEGIN@6 at line 14 # spent 101µs making 1 call to Hailo::BEGIN@14
# spent 85µs making 1 call to Exporter::import |
15 | 2 | 169µs | 2 | 21.8ms | # spent 20.3ms (937µs+19.4) within Hailo::BEGIN@15 which was called:
# once (937µs+19.4ms) by main::BEGIN@6 at line 15 # spent 20.3ms making 1 call to Hailo::BEGIN@15
# spent 1.55ms making 1 call to namespace::clean::import |
16 | |||||
17 | 1 | 10µs | 1 | 78µs | # spent 92µs (14+78) within Hailo::BEGIN@17 which was called:
# once (14µs+78µs) by main::BEGIN@6 at line 25 # spent 78µs making 1 call to constant::import |
18 | Hailo::Engine::Default | ||||
19 | Hailo::Storage::MySQL | ||||
20 | Hailo::Storage::PostgreSQL | ||||
21 | Hailo::Storage::SQLite | ||||
22 | Hailo::Tokenizer::Chars | ||||
23 | Hailo::Tokenizer::Words | ||||
24 | Hailo::UI::ReadLine | ||||
25 | 1 | 342µs | 1 | 92µs | ] ]; # spent 92µs making 1 call to Hailo::BEGIN@17 |
26 | |||||
27 | 1 | 5µs | 1 | 431µs | has brain => ( # spent 431µs making 1 call to Mouse::has |
28 | isa => 'Str', | ||||
29 | is => 'rw', | ||||
30 | ); | ||||
31 | |||||
32 | has order => ( | ||||
33 | isa => 'Int', | ||||
34 | is => 'rw', | ||||
35 | default => 2, | ||||
36 | trigger => sub { | ||||
37 | my ($self, $order) = @_; | ||||
38 | $self->_custom_order(1); | ||||
39 | }, | ||||
40 | 1 | 6µs | 1 | 276µs | ); # spent 276µs making 1 call to Mouse::has |
41 | |||||
42 | 1 | 4µs | 1 | 262µs | has _custom_order => ( # spent 262µs making 1 call to Mouse::has |
43 | isa => 'Bool', | ||||
44 | is => 'rw', | ||||
45 | default => 0, | ||||
46 | init_arg => undef, | ||||
47 | documentation => "Here so we can differentiate between the default value of order being explictly set and being set by default", | ||||
48 | ); | ||||
49 | |||||
50 | 1 | 4µs | 1 | 286µs | has _custom_tokenizer_class => ( # spent 286µs making 1 call to Mouse::has |
51 | isa => 'Bool', | ||||
52 | is => 'rw', | ||||
53 | default => 0, | ||||
54 | init_arg => undef, | ||||
55 | documentation => "Here so we can differentiate between the default value of tokenizer_class being explictly set and being set by default", | ||||
56 | ); | ||||
57 | |||||
58 | 1 | 4µs | 1 | 262µs | has save_on_exit => ( # spent 262µs making 1 call to Mouse::has |
59 | isa => 'Bool', | ||||
60 | is => 'rw', | ||||
61 | default => 1, | ||||
62 | ); | ||||
63 | |||||
64 | has brain_resource => ( | ||||
65 | documentation => "Alias for `brain' for backwards compatibility", | ||||
66 | isa => 'Str', | ||||
67 | is => 'rw', | ||||
68 | # spent 18µs (10+8) within Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:71] which was called:
# once (10µs+8µs) by Mouse::Object::new at line 9 of reply.pl | ||||
69 | 1 | 2µs | my ($self, $brain) = @_; | ||
70 | 1 | 18µs | 2 | 10µs | $self->brain($brain); # spent 8µs making 1 call to Hailo::brain
# spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint |
71 | }, | ||||
72 | 1 | 5µs | 1 | 266µs | ); # spent 266µs making 1 call to Mouse::has |
73 | |||||
74 | 1 | 5µs | my %has = ( | ||
75 | engine => { | ||||
76 | name => 'Engine', | ||||
77 | default => 'Default', | ||||
78 | }, | ||||
79 | storage => { | ||||
80 | name => 'Storage', | ||||
81 | default => 'SQLite', | ||||
82 | }, | ||||
83 | tokenizer => { | ||||
84 | name => 'Tokenizer', | ||||
85 | default => 'Words', | ||||
86 | }, | ||||
87 | ui => { | ||||
88 | name => 'UI', | ||||
89 | default => 'ReadLine', | ||||
90 | }, | ||||
91 | ); | ||||
92 | |||||
93 | 1 | 3µs | for my $k (keys %has) { | ||
94 | 4 | 7µs | my $name = $has{$k}->{name}; | ||
95 | 4 | 5µs | my $default = $has{$k}->{default}; | ||
96 | 4 | 5µs | my $method_class = "${k}_class"; | ||
97 | 4 | 4µs | my $method_args = "${k}_args"; | ||
98 | |||||
99 | # working classes | ||||
100 | has "${k}_class" => ( | ||||
101 | isa => 'Str', | ||||
102 | is => "rw", | ||||
103 | default => $default, | ||||
104 | ($k ~~ 'tokenizer' | ||||
105 | ? (trigger => sub { | ||||
106 | my ($self, $class) = @_; | ||||
107 | $self->_custom_tokenizer_class(1); | ||||
108 | }) | ||||
109 | 4 | 23µs | 4 | 1.07ms | : ()) # spent 1.07ms making 4 calls to Mouse::has, avg 268µs/call |
110 | ); | ||||
111 | |||||
112 | # Object arguments | ||||
113 | has "${k}_args" => ( | ||||
114 | documentation => "Arguments for the $name class", | ||||
115 | isa => 'HashRef', | ||||
116 | is => "ro", | ||||
117 | 3 | 15µs | # spent 11µs within Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:117] which was called 3 times, avg 4µs/call:
# 3 times (11µs+0s) by Mouse::Object::new at line 9 of reply.pl, avg 4µs/call | ||
118 | 4 | 28µs | 4 | 1.08ms | ); # spent 1.08ms making 4 calls to Mouse::has, avg 269µs/call |
119 | |||||
120 | # Working objects | ||||
121 | 4 | 16µs | 4 | 2.12ms | has "_${k}" => ( # spent 2.12ms making 4 calls to Mouse::has, avg 531µs/call |
122 | does => "Hailo::Role::$name", | ||||
123 | lazy_build => 1, | ||||
124 | is => 'ro', | ||||
125 | init_arg => undef, | ||||
126 | ); | ||||
127 | |||||
128 | # Generate the object itself | ||||
129 | 2 | 333µs | 2 | 60µs | # spent 36µs (11+25) within Hailo::BEGIN@129 which was called:
# once (11µs+25µs) by main::BEGIN@6 at line 129 # spent 36µs making 1 call to Hailo::BEGIN@129
# spent 25µs making 1 call to strict::unimport |
130 | # spent 117ms (166µs+117) within Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] which was called 3 times, avg 39.1ms/call:
# once (84µs+52.5ms) by Hailo::_storage at line 309
# once (30µs+47.8ms) by Hailo::_tokenizer at line 317
# once (53µs+17.0ms) by Hailo::_engine at line 316 | ||||
131 | 3 | 6µs | my ($self) = @_; | ||
132 | |||||
133 | my $obj = $self->_new_class( | ||||
134 | $name, | ||||
135 | $self->$method_class, | ||||
136 | { | ||||
137 | arguments => $self->$method_args, | ||||
138 | ($k ~~ [ qw< engine storage > ] | ||||
139 | ? (order => $self->order) | ||||
140 | : ()), | ||||
141 | ($k ~~ [ qw< engine > ] | ||||
142 | ? (storage => $self->_storage) | ||||
143 | : ()), | ||||
144 | (($k ~~ [ qw< storage > ] and defined $self->brain) | ||||
145 | ? ( | ||||
146 | 3 | 143µs | 15 | 117ms | hailo => do { # spent 117ms making 3 calls to Hailo::_new_class, avg 39.1ms/call
# spent 4µs making 2 calls to Hailo::tokenizer_class, avg 2µs/call
# spent 3µs making 2 calls to Hailo::brain, avg 2µs/call
# spent 3µs making 2 calls to Hailo::order, avg 2µs/call
# spent 2µs making 1 call to Hailo::engine_class
# spent 2µs making 1 call to Hailo::_storage
# spent 2µs making 1 call to Hailo::engine_args
# spent 2µs making 1 call to Hailo::storage_class
# spent 2µs making 1 call to Hailo::tokenizer_args
# spent 1µs making 1 call to Hailo::storage_args |
147 | 1 | 2µs | require Scalar::Util; | ||
148 | 1 | 8µs | 1 | 3µs | Scalar::Util::weaken(my $s = $self); # spent 3µs making 1 call to Scalar::Util::weaken |
149 | |||||
150 | my %callback = ( | ||||
151 | has_custom_order => sub { $s->_custom_order }, | ||||
152 | has_custom_tokenizer_class => sub { $s->_custom_tokenizer_class }, | ||||
153 | set_order => sub { | ||||
154 | my ($db_order) = @_; | ||||
155 | $s->order($db_order); | ||||
156 | $s->_engine->order($db_order); | ||||
157 | }, | ||||
158 | set_tokenizer_class => sub { | ||||
159 | my ($db_tokenizer_class) = @_; | ||||
160 | $s->tokenizer_class($db_tokenizer_class); | ||||
161 | }, | ||||
162 | 1 | 12µs | ); | ||
163 | |||||
164 | 1 | 2µs | \%callback; | ||
165 | }, | ||||
166 | brain => $self->brain | ||||
167 | ) | ||||
168 | : ()), | ||||
169 | (($k ~~ [ qw< storage > ] | ||||
170 | ? (tokenizer_class => $self->tokenizer_class) | ||||
171 | : ())) | ||||
172 | }, | ||||
173 | ); | ||||
174 | |||||
175 | 3 | 17µs | return $obj; | ||
176 | 4 | 34µs | }; | ||
177 | } | ||||
178 | |||||
179 | # spent 117ms (5.67+112) within Hailo::_new_class which was called 3 times, avg 39.1ms/call:
# 3 times (5.67ms+112ms) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146, avg 39.1ms/call | ||||
180 | 3 | 7µs | my ($self, $type, $class, $args) = @_; | ||
181 | |||||
182 | 3 | 2µs | my $pkg; | ||
183 | 3 | 29µs | 3 | 10µs | if ($class =~ m[^\+(?<custom_plugin>.+)$]) { # spent 10µs making 3 calls to Hailo::CORE:match, avg 3µs/call |
184 | 2 | 1.18ms | 1 | 279µs | # spent 279µs (259+19) within Hailo::BEGIN@184 which was called:
# once (259µs+19µs) by main::BEGIN@6 at line 184 # spent 279µs making 1 call to Hailo::BEGIN@184 |
185 | } else { | ||||
186 | 3 | 35µs | 3 | 5µs | my @plugins = @{ $self->PLUGINS }; # spent 5µs making 3 calls to Hailo::PLUGINS, avg 2µs/call |
187 | # Be fuzzy about includes, e.g. DBD::SQLite or SQLite or sqlite will go | ||||
188 | 13 | 191µs | 26 | 90µs | $pkg = first { / $type : .* : $class /ix } # spent 58µs making 13 calls to Hailo::CORE:regcomp, avg 4µs/call
# spent 32µs making 13 calls to Hailo::CORE:match, avg 2µs/call |
189 | 3 | 54µs | 6 | 204µs | sort { length $a <=> length $b } # spent 185µs making 3 calls to List::Util::first, avg 62µs/call
# spent 19µs making 3 calls to Hailo::CORE:sort, avg 6µs/call |
190 | @plugins; | ||||
191 | |||||
192 | 3 | 6µs | unless ($pkg) { | ||
193 | local $" = ', '; | ||||
194 | my @p = grep { /$type/ } @plugins; | ||||
195 | die "Couldn't find a class name matching '$class' in plugins '@p'"; | ||||
196 | } | ||||
197 | } | ||||
198 | |||||
199 | 3 | 28µs | 3 | 15µs | if (Any::Moose::moose_is_preferred()) { # spent 15µs making 3 calls to Any::Moose::moose_is_preferred, avg 5µs/call |
200 | require Class::MOP; | ||||
201 | eval { Class::MOP::load_class($pkg) }; | ||||
202 | } else { | ||||
203 | 3 | 151µs | eval qq[require $pkg]; # spent 242µs executing statements in string eval
# spent 164µs executing statements in string eval
# spent 152µs executing statements in string eval | ||
204 | } | ||||
205 | 3 | 4µs | die $@ if $@; | ||
206 | |||||
207 | 3 | 230µs | 30 | 1.05ms | return $pkg->new(%$args); # spent 614µs making 3 calls to Mouse::Object::new, avg 205µs/call
# spent 296µs making 3 calls to Mouse::Meta::Class::_calculate_all_attributes, avg 99µs/call
# spent 79µs making 1 call to Hailo::Engine::Default::BUILD
# spent 26µs making 1 call to Hailo::Role::Tokenizer::BUILD
# spent 15µs making 10 calls to Mouse::Meta::TypeConstraint::_compiled_type_constraint, avg 1µs/call
# spent 7µs making 3 calls to Mouse::Meta::Class::strict_constructor, avg 2µs/call
# spent 5µs making 1 call to Hailo::Role::Tokenizer::__ANON__[Hailo/Role/Tokenizer.pm:21]
# spent 5µs making 3 calls to Mouse::Meta::Class::is_immutable, avg 2µs/call
# spent 4µs making 3 calls to Mouse::Meta::Class::is_anon_class, avg 1µs/call
# spent 3µs making 2 calls to Mouse::Meta::Attribute::default, avg 1µs/call |
208 | } | ||||
209 | |||||
210 | # spent 3.92s (18µs+3.92) within Hailo::save which was called:
# once (18µs+3.92s) by Hailo::DEMOLISH at line 340 | ||||
211 | 1 | 2µs | my ($self, @args) = @_; | ||
212 | 1 | 10µs | 2 | 3.92s | $self->_storage->save(@args); # spent 3.92s making 1 call to Hailo::Storage::SQLite::save
# spent 2µs making 1 call to Hailo::_storage |
213 | 1 | 7µs | return; | ||
214 | } | ||||
215 | |||||
216 | sub train { | ||||
217 | my ($self, $input) = @_; | ||||
218 | |||||
219 | $self->_storage->start_training(); | ||||
220 | |||||
221 | given ($input) { | ||||
222 | # With STDIN | ||||
223 | when (not ref and defined and $_ eq '-') { | ||||
224 | die "You must provide STDIN when training from '-'" if $self->_is_interactive(*STDIN); | ||||
225 | $self->_train_fh(*STDIN); | ||||
226 | } | ||||
227 | # With a filehandle | ||||
228 | when (ref eq 'GLOB') { | ||||
229 | $self->_train_fh($input); | ||||
230 | } | ||||
231 | # With a file | ||||
232 | when (not ref) { | ||||
233 | open my $fh, '<:encoding(utf8)', $input; | ||||
234 | $self->_train_fh($fh, $input); | ||||
235 | } | ||||
236 | # With an Array | ||||
237 | when (ref eq 'ARRAY') { | ||||
238 | $self->_learn_one($_) for @$input; | ||||
239 | } | ||||
240 | # With something naughty | ||||
241 | default { | ||||
242 | die "Unknown input: $input"; | ||||
243 | } | ||||
244 | } | ||||
245 | |||||
246 | $self->_storage->stop_training(); | ||||
247 | |||||
248 | return; | ||||
249 | } | ||||
250 | |||||
251 | sub _train_fh { | ||||
252 | my ($self, $fh, $filename) = @_; | ||||
253 | |||||
254 | while (my $line = <$fh>) { | ||||
255 | chomp $line; | ||||
256 | $self->_learn_one($line); | ||||
257 | } | ||||
258 | |||||
259 | return; | ||||
260 | } | ||||
261 | |||||
262 | sub learn { | ||||
263 | my ($self, $input) = @_; | ||||
264 | my $inputs; | ||||
265 | |||||
266 | given ($input) { | ||||
267 | when (not defined) { | ||||
268 | die "Cannot learn from undef input"; | ||||
269 | } | ||||
270 | when (not ref) { | ||||
271 | $inputs = [$input]; | ||||
272 | } | ||||
273 | # With an Array | ||||
274 | when (ref eq 'ARRAY') { | ||||
275 | $inputs = $input | ||||
276 | } | ||||
277 | default { | ||||
278 | die "Unknown input: $input"; | ||||
279 | } | ||||
280 | } | ||||
281 | |||||
282 | my $storage = $self->_storage; | ||||
283 | |||||
284 | $storage->start_learning(); | ||||
285 | $self->_learn_one($_) for @$inputs; | ||||
286 | $storage->stop_learning(); | ||||
287 | return; | ||||
288 | } | ||||
289 | |||||
290 | sub _learn_one { | ||||
291 | my ($self, $input) = @_; | ||||
292 | my $engine = $self->_engine; | ||||
293 | |||||
294 | my $tokens = $self->_tokenizer->make_tokens($input); | ||||
295 | $engine->learn($tokens); | ||||
296 | |||||
297 | return; | ||||
298 | } | ||||
299 | |||||
300 | sub learn_reply { | ||||
301 | my ($self, $input) = @_; | ||||
302 | $self->learn($input); | ||||
303 | return $self->reply($input); | ||||
304 | } | ||||
305 | |||||
306 | # spent 132s (1.12+131) within Hailo::reply which was called 30000 times, avg 4.40ms/call:
# 30000 times (1.12s+131s) by main::RUNTIME at line 17 of reply.pl, avg 4.40ms/call | ||||
307 | 30000 | 48.9ms | my ($self, $input) = @_; | ||
308 | |||||
309 | 30000 | 183ms | 30004 | 169ms | my $storage = $self->_storage; # spent 117ms making 30000 calls to Hailo::_storage, avg 4µs/call
# spent 52.6ms making 1 call to Hailo::__ANON__[Hailo.pm:176]
# spent 117µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217]
# spent 2µs making 1 call to Mouse::Meta::Attribute::builder
# spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint |
310 | # start_training() hasn't been called so we can't guarentee that | ||||
311 | # the storage has been engaged at this point. This must be called | ||||
312 | # before ->_engine() is called anywhere to ensure that the | ||||
313 | # lazy-loading in the engine works. | ||||
314 | 30000 | 160ms | 30001 | 441ms | $storage->_engage() unless $storage->_engaged; # spent 389ms making 1 call to Hailo::Storage::SQLite::_engage
# spent 52.0ms making 30000 calls to Hailo::Storage::_engaged, avg 2µs/call |
315 | |||||
316 | 30000 | 157ms | 30004 | 80.2ms | my $engine = $self->_engine; # spent 63.0ms making 30000 calls to Hailo::_engine, avg 2µs/call
# spent 17.1ms making 1 call to Hailo::__ANON__[Hailo.pm:176]
# spent 115µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217]
# spent 2µs making 1 call to Mouse::Meta::Attribute::builder
# spent 2µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint |
317 | 30000 | 153ms | 30004 | 142ms | my $tokenizer = $self->_tokenizer; # spent 94.2ms making 30000 calls to Hailo::_tokenizer, avg 3µs/call
# spent 47.8ms making 1 call to Hailo::__ANON__[Hailo.pm:176]
# spent 115µs making 1 call to Mouse::Util::TypeConstraints::__ANON__[Mouse/Util/TypeConstraints.pm:217]
# spent 2µs making 1 call to Mouse::Meta::Attribute::builder
# spent 1µs making 1 call to Mouse::Meta::TypeConstraint::_compiled_type_constraint |
318 | |||||
319 | 30000 | 25.9ms | my $reply; | ||
320 | 30000 | 75.6ms | if (defined $input) { | ||
321 | my $tokens = $tokenizer->make_tokens($input); | ||||
322 | $reply = $engine->reply($tokens); | ||||
323 | } | ||||
324 | else { | ||||
325 | 30000 | 127ms | 30000 | 120s | $reply = $engine->reply(); # spent 120s making 30000 calls to Hailo::Engine::Default::reply, avg 4.00ms/call |
326 | } | ||||
327 | |||||
328 | 30000 | 34.1ms | return unless defined $reply; | ||
329 | 30000 | 315ms | 30000 | 10.4s | return $tokenizer->make_output($reply); # spent 10.4s making 30000 calls to Hailo::Tokenizer::Words::make_output, avg 346µs/call |
330 | } | ||||
331 | |||||
332 | sub stats { | ||||
333 | my ($self) = @_; | ||||
334 | |||||
335 | return $self->_storage->totals(); | ||||
336 | } | ||||
337 | |||||
338 | # spent 3.92s (26µs+3.92) within Hailo::DEMOLISH which was called:
# once (26µs+3.92s) by Mouse::Object::DESTROY at line 0 of reply.pl | ||||
339 | 1 | 2µs | my ($self) = @_; | ||
340 | 1 | 21µs | 3 | 3.92s | $self->save() if blessed $self->{_storage} and $self->save_on_exit; # spent 3.92s making 1 call to Hailo::save
# spent 3µs making 1 call to Scalar::Util::blessed
# spent 2µs making 1 call to Hailo::save_on_exit |
341 | 1 | 16µs | return; | ||
342 | } | ||||
343 | |||||
344 | sub _is_interactive { | ||||
345 | require IO::Interactive; | ||||
346 | return IO::Interactive::is_interactive(); | ||||
347 | } | ||||
348 | |||||
349 | 1 | 32µs | 2 | 157µs | __PACKAGE__->meta->make_immutable; # spent 142µs making 1 call to Mouse::Meta::Class::make_immutable
# spent 14µs making 1 call to Hailo::meta |
350 | |||||
351 | =encoding utf8 | ||||
352 | |||||
353 | =head1 NAME | ||||
354 | |||||
355 | Hailo - A pluggable Markov engine analogous to MegaHAL | ||||
356 | |||||
357 | =head1 SYNOPSIS | ||||
358 | |||||
359 | This is the synopsis for using Hailo as a module. See L<hailo> for | ||||
360 | command-line invocation. | ||||
361 | |||||
362 | # Hailo requires Perl 5.10 | ||||
363 | use 5.010; | ||||
364 | use Any::Moose; | ||||
365 | use Hailo; | ||||
366 | |||||
367 | # Construct a new in-memory Hailo using the SQLite backend. See | ||||
368 | # backend documentation for other options. | ||||
369 | my $hailo = Hailo->new; | ||||
370 | |||||
371 | # Various ways to learn | ||||
372 | my @train_this = ("I like big butts", "and I can not lie"); | ||||
373 | $hailo->learn(\@train_this); | ||||
374 | $hailo->learn($_) for @train_this; | ||||
375 | |||||
376 | # Heavy-duty training interface. Backends may drop some safety | ||||
377 | # features like journals or synchronous IO to train faster using | ||||
378 | # this mode. | ||||
379 | $hailo->train("megahal.trn"); | ||||
380 | $hailo->train($filehandle); | ||||
381 | |||||
382 | # Make the brain babble | ||||
383 | say $hailo->reply("hello good sir."); | ||||
384 | # Just say something at random | ||||
385 | say $hailo->reply(); | ||||
386 | |||||
387 | =head1 DESCRIPTION | ||||
388 | |||||
389 | Hailo is a fast and lightweight markov engine intended to replace | ||||
390 | L<AI::MegaHAL|AI::MegaHAL>. It has a L<Mouse|Mouse> (or | ||||
391 | L<Moose|Moose>) based core with pluggable | ||||
392 | L<storage|Hailo::Role::Storage>, L<tokenizer|Hailo::Role::Tokenizer> | ||||
393 | and L<engine|Hailo::Role::Engine> backends. | ||||
394 | |||||
395 | It is similar to MegaHAL in functionality, the main differences (with the | ||||
396 | default backends) being better scalability, drastically less memory usage, | ||||
397 | an improved tokenizer, and tidier output. | ||||
398 | |||||
399 | With this distribution, you can create, modify, and query Hailo brains. To | ||||
400 | use Hailo in event-driven POE applications, you can use the | ||||
401 | L<POE::Component::Hailo|POE::Component::Hailo> wrapper. One example is | ||||
402 | L<POE::Component::IRC::Plugin::Hailo|POE::Component::IRC::Plugin::Hailo>, | ||||
403 | which implements an IRC chat bot. | ||||
404 | |||||
405 | =head2 Etymology | ||||
406 | |||||
407 | I<Hailo> is a portmanteau of I<HAL> (as in MegaHAL) and | ||||
408 | L<failo|http://identi.ca/failo>. | ||||
409 | |||||
410 | =head1 Backends | ||||
411 | |||||
412 | Hailo supports pluggable L<storage|Hailo::Role::Storage> and | ||||
413 | L<tokenizer|Hailo::Role::Tokenizer> backends, it also supports a | ||||
414 | pluggable L<UI|Hailo::Role::UI> backend which is used by the L<hailo> | ||||
415 | command-line utility. | ||||
416 | |||||
417 | =head2 Storage | ||||
418 | |||||
419 | Hailo can currently store its data in either a | ||||
420 | L<SQLite|Hailo::Storage::SQLite>, | ||||
421 | L<PostgreSQL|Hailo::Storage::PostgreSQL> or | ||||
422 | L<MySQL|Hailo::Storage::MySQL> database. Some NoSQL backends were | ||||
423 | supported in earlier versions, but they were removed as they had no | ||||
424 | redeeming quality. | ||||
425 | |||||
426 | SQLite is the primary target for Hailo. It's much faster and uses less | ||||
427 | resources than the other two. It's highly recommended that you use it. | ||||
428 | |||||
429 | See L<Hailo::Storage/"Comparison of backends"> for benchmarks showing | ||||
430 | how the various backends compare under different workloads, and how | ||||
431 | you can create your own. | ||||
432 | |||||
433 | =head2 Tokenizer | ||||
434 | |||||
435 | By default Hailo will use L<the word | ||||
436 | tokenizer|Hailo::Tokenizer::Words> to split up input by whitespace, | ||||
437 | taking into account things like quotes, sentence terminators and more. | ||||
438 | |||||
439 | There's also a L<the character | ||||
440 | tokenizer|Hailo::Tokenizer::Chars>. It's not generally useful for a | ||||
441 | conversation bot but can be used to e.g. generate new words given a | ||||
442 | list of existing words. | ||||
443 | |||||
444 | =head1 UPGRADING | ||||
445 | |||||
446 | Hailo makes no promises about brains generated with earlier versions | ||||
447 | being compatable with future version and due to the way Hailo works | ||||
448 | there's no practical way to make that promise. Learning in Hailo is | ||||
449 | lossy so an accurate conversion is impossible. | ||||
450 | |||||
451 | If you're maintaining a Hailo brain that you want to keep using you | ||||
452 | should save the input you trained it on and re-train when you upgrade. | ||||
453 | |||||
454 | Hailo is always going to lose information present in the input you | ||||
455 | give it. How input tokens get split up and saved to the storage | ||||
456 | backend depends on the version of the tokenizer being used and how | ||||
457 | that input gets saved to the database. | ||||
458 | |||||
459 | For instance if an earlier version of Hailo tokenized C<"foo+bar"> | ||||
460 | simply as C<"foo+bar"> but a later version split that up into | ||||
461 | C<"foo", "+", "bar">, then an input of C<"foo+bar are my favorite | ||||
462 | metasyntactic variables"> wouldn't take into account the existing | ||||
463 | C<"foo+bar"> string in the database. | ||||
464 | |||||
465 | Tokenizer changes like this would cause the brains to accumulate | ||||
466 | garbage and would leave other parts in a state they wouldn't otherwise | ||||
467 | have gotten into. | ||||
468 | |||||
469 | There have been more drastic changes to the database format itself in | ||||
470 | the past. | ||||
471 | |||||
472 | Having said all that the database format and the tokenizer are | ||||
473 | relatively stable. At the time of writing 0.33 is the latest release | ||||
474 | and it's compatable with brains down to at least 0.17. If you're | ||||
475 | upgrading and there isn't a big notice about the storage format being | ||||
476 | incompatable in the F<Changes> file your old brains will probably work | ||||
477 | just fine. | ||||
478 | |||||
479 | =head1 ATTRIBUTES | ||||
480 | |||||
481 | =head2 C<brain> | ||||
482 | |||||
483 | The name of the brain (file name, database name) to use as storage. | ||||
484 | There is no default. Whether this gets used at all depends on the | ||||
485 | storage backend, currently only SQLite uses it. | ||||
486 | |||||
487 | =head2 C<save_on_exit> | ||||
488 | |||||
489 | A boolean value indicating whether Hailo should save its state before | ||||
490 | its object gets destroyed. This defaults to true and will simply call | ||||
491 | L<save|/save> at C<DEMOLISH> time. | ||||
492 | |||||
493 | See L<Hailo::Storage::SQLite/"in_memory"> for how the SQLite backend | ||||
494 | uses this option in conjunction with its C<in_memory> option. | ||||
495 | |||||
496 | =head2 C<order> | ||||
497 | |||||
498 | The Markov order (chain length) you want to use for an empty brain. | ||||
499 | The default is 2. | ||||
500 | |||||
501 | =head2 C<engine_class> | ||||
502 | |||||
503 | =head2 C<storage_class> | ||||
504 | |||||
505 | =head2 C<tokenizer_class> | ||||
506 | |||||
507 | =head2 C<ui_class> | ||||
508 | |||||
509 | A a short name name of the class we use for the engine, storage, | ||||
510 | tokenizer or ui backends. | ||||
511 | |||||
512 | By default this is B<Default> for the engine, B<SQLite> for storage, | ||||
513 | B<Words> for the tokenizer and B<ReadLine> for the UI. The UI backend | ||||
514 | is only used by the L<hailo> command-line interface. | ||||
515 | |||||
516 | You can only specify the short name of one of the packages Hailo | ||||
517 | itself ships with. If you need another class then just prefix the | ||||
518 | package with a plus (Catalyst style), e.g. C<+My::Foreign::Tokenizer>. | ||||
519 | |||||
520 | =head2 C<engine_args> | ||||
521 | |||||
522 | =head2 C<storage_args> | ||||
523 | |||||
524 | =head2 C<tokenizer_args> | ||||
525 | |||||
526 | =head2 C<ui_args> | ||||
527 | |||||
528 | A C<HashRef> of arguments for engine/storage/tokenizer/ui | ||||
529 | backends. See the documentation for the backends for what sort of | ||||
530 | arguments they accept. | ||||
531 | |||||
532 | =head1 METHODS | ||||
533 | |||||
534 | =head2 C<new> | ||||
535 | |||||
536 | This is the constructor. It accepts the attributes specified in | ||||
537 | L</ATTRIBUTES>. | ||||
538 | |||||
539 | =head2 C<learn> | ||||
540 | |||||
541 | Takes a string or an array reference of strings and learns from them. | ||||
542 | |||||
543 | =head2 C<train> | ||||
544 | |||||
545 | Takes a filename, filehandle or array reference and learns from all its | ||||
546 | lines. If a filename is passed, the file is assumed to be UTF-8 encoded. | ||||
547 | Unlike L<C<learn>|/learn>, this method sacrifices some safety (disables | ||||
548 | the database journal, fsyncs, etc) for speed while learning. | ||||
549 | |||||
550 | =head2 C<reply> | ||||
551 | |||||
552 | Takes an optional line of text and generates a reply that might be relevant. | ||||
553 | |||||
554 | =head2 C<learn_reply> | ||||
555 | |||||
556 | Takes a string argument, learns from it, and generates a reply that | ||||
557 | might be relevant. This is equivalent to calling L<learn|/learn> | ||||
558 | followed by L<reply|/reply>. | ||||
559 | |||||
560 | =head2 C<save> | ||||
561 | |||||
562 | Tells the underlying storage backend to L<save its | ||||
563 | state|Hailo::Role::Storage/"save">, any arguments to this method will | ||||
564 | be passed as-is to the backend. | ||||
565 | |||||
566 | =head2 C<stats> | ||||
567 | |||||
568 | Takes no arguments. Returns the number of tokens, expressions, previous | ||||
569 | token links and next token links. | ||||
570 | |||||
571 | =head1 SUPPORT | ||||
572 | |||||
573 | You can join the IRC channel I<#hailo> on FreeNode if you have questions. | ||||
574 | |||||
575 | =head1 BUGS | ||||
576 | |||||
577 | Bugs, feature requests and other issues are tracked in L<Hailo's RT on | ||||
578 | rt.cpan.org|https://rt.cpan.org/Dist/Display.html?Name=Hailo> | ||||
579 | |||||
580 | =head1 SEE ALSO | ||||
581 | |||||
582 | =over | ||||
583 | |||||
584 | =item * L<POE::Component::Hailo> - A non-blocking POE wrapper around Hailo | ||||
585 | |||||
586 | =item * L<POE::Component::IRC::Plugin::Hailo> - A Hailo IRC bot plugin | ||||
587 | |||||
588 | =item * L<http://github.com/hinrik/failo> - Failo, an IRC bot that uses Hailo | ||||
589 | |||||
590 | =item * L<http://github.com/bingos/gumbybrain> - GumbyBRAIN, a more famous IRC bot that uses Hailo | ||||
591 | |||||
592 | =item * L<Hailo::UI::Web> - A L<Catalyst> and jQuery powered web | ||||
593 | interface to Hailo available at L<hailo.nix.is|http://hailo.nix.is> | ||||
594 | and as L<hailo-ui-web|http://github.com/avar/hailo-ui-web> on | ||||
595 | L<GitHub|http://github.com> | ||||
596 | |||||
597 | =item * L<HALBot> - Another L<Catalyst> Dojo powered web interface to | ||||
598 | Hailo available at L<bifurcat.es|http://bifurcat.es/> and as | ||||
599 | L<halbot-on-the-web|http://gitorious.org/halbot-on-the-web/halbot-on-the-web> | ||||
600 | at L<gitorious|http://gitorious.org> | ||||
601 | |||||
602 | =item * L<http://github.com/pteichman/cobe> - cobe, a Python port of MegaHAL "inspired by the success of Hailo" | ||||
603 | |||||
604 | =back | ||||
605 | |||||
606 | =head1 LINKS | ||||
607 | |||||
608 | =over | ||||
609 | |||||
610 | =item * L<hailo.org|http://hailo.org> - Hailo's website | ||||
611 | |||||
612 | =item * L<http://bit.ly/hailo_rewrite_of_megahal> - Hailo: A Perl rewrite of | ||||
613 | MegaHAL, A blog posting about the motivation behind Hailo | ||||
614 | |||||
615 | =item * L<http://blogs.perl.org/users/aevar_arnfjor_bjarmason/hailo/> - | ||||
616 | More blog posts about Hailo on E<AElig>var ArnfjE<ouml>rE<eth> | ||||
617 | Bjarmason's L<blogs.perl.org|http://blogs.perl.org> blog | ||||
618 | |||||
619 | =item * Hailo on L<freshmeat|http://freshmeat.net/projects/hailo> and | ||||
620 | L<ohloh|https://www.ohloh.net/p/hailo> | ||||
621 | |||||
622 | =back | ||||
623 | |||||
624 | =head1 AUTHORS | ||||
625 | |||||
626 | Hinrik E<Ouml>rn SigurE<eth>sson, hinrik.sig@gmail.com | ||||
627 | |||||
628 | E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org> | ||||
629 | |||||
630 | =head1 LICENSE AND COPYRIGHT | ||||
631 | |||||
632 | Copyright 2010 Hinrik E<Ouml>rn SigurE<eth>sson and | ||||
633 | E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avar@cpan.org> | ||||
634 | |||||
635 | This program is free software, you can redistribute it and/or modify | ||||
636 | it under the same terms as Perl itself. | ||||
637 | |||||
638 | 1 | 21µs | 1 | 4.66ms | =cut # spent 4.66ms making 1 call to B::Hooks::EndOfScope::__ANON__[B/Hooks/EndOfScope.pm:26] |
sub Hailo::CORE:match; # opcode | |||||
# spent 58µs within Hailo::CORE:regcomp which was called 13 times, avg 4µs/call:
# 13 times (58µs+0s) by List::Util::first at line 188, avg 4µs/call | |||||
# spent 19µs within Hailo::CORE:sort which was called 3 times, avg 6µs/call:
# 3 times (19µs+0s) by Hailo::_new_class at line 189, avg 6µs/call | |||||
# spent 5µs within Hailo::PLUGINS which was called 3 times, avg 2µs/call:
# 3 times (5µs+0s) by Hailo::_new_class at line 186, avg 2µs/call | |||||
# spent 63.0ms (45.8+17.2) within Hailo::_engine which was called 30000 times, avg 2µs/call:
# 30000 times (45.8ms+17.2ms) by Hailo::reply at line 316, avg 2µs/call | |||||
# spent 117ms (64.0+52.7) within Hailo::_storage which was called 30002 times, avg 4µs/call:
# 30000 times (64.0ms+52.7ms) by Hailo::reply at line 309, avg 4µs/call
# once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146
# once (2µs+0s) by Hailo::save at line 212 | |||||
# spent 94.2ms (46.3+47.9) within Hailo::_tokenizer which was called 30000 times, avg 3µs/call:
# 30000 times (46.3ms+47.9ms) by Hailo::reply at line 317, avg 3µs/call | |||||
# spent 12µs (10+2) within Hailo::brain which was called 3 times, avg 4µs/call:
# 2 times (3µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146, avg 2µs/call
# once (7µs+2µs) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:71] at line 70 | |||||
# spent 2µs within Hailo::engine_args which was called:
# once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146 | |||||
# spent 2µs within Hailo::engine_class which was called:
# once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146 | |||||
# spent 3µs within Hailo::order which was called 2 times, avg 2µs/call:
# 2 times (3µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146, avg 2µs/call | |||||
# spent 2µs within Hailo::save_on_exit which was called:
# once (2µs+0s) by Hailo::DEMOLISH at line 340 | |||||
# spent 1µs within Hailo::storage_args which was called:
# once (1µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146 | |||||
# spent 2µs within Hailo::storage_class which was called:
# once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146 | |||||
# spent 2µs within Hailo::tokenizer_args which was called:
# once (2µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146 | |||||
# spent 4µs within Hailo::tokenizer_class which was called 2 times, avg 2µs/call:
# 2 times (4µs+0s) by Hailo::__ANON__[/home/hinrik/perl5/perlbrew/perls/perl-5.13.5/lib/site_perl/5.13.5/Hailo.pm:176] at line 146, avg 2µs/call |