lab42_core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +114 -1
- data/lib/lab42/core/hash.rb +8 -0
- data/lib/lab42/core/memoization.rb +14 -0
- data/lib/lab42/core/version.rb +1 -1
- data/lib/lab42/core.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50b79fb53cb3eabf40d9a75db01d945d338b5dfc
|
4
|
+
data.tar.gz: 60df674bd40c480f1dbfd62ac5c11b0a3bf5a7ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65fa1b525bee1585344538b8d1b86fcc657d62803c1ee7afd74f36e0ae5c26cdf1761bc617aac5679f856315a8ce746cd5ca3b893cc72225548e1114b5cb5e64
|
7
|
+
data.tar.gz: 169ec69933e737e28809724d47d1e71816f6a927fb3649f76538310c1bd0ada7869fad22f726f29f6fe9c2e762ec791e1dfb60e95f41d622bcfeb969abbc4bb0
|
data/README.md
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
|
2
2
|
|
3
|
-
|
4
3
|
# lab42\_core
|
5
4
|
|
6
5
|
|
@@ -11,6 +10,107 @@
|
|
11
10
|
|
12
11
|
Simple Ruby Core Module Extensions (for more see lab42\_more)
|
13
12
|
|
13
|
+
## Programming Paradigms
|
14
|
+
|
15
|
+
### Memoization and Lazy Attributes
|
16
|
+
|
17
|
+
#### Memoization
|
18
|
+
|
19
|
+
is a, slightly forgotten, programming technique protecting against double calcultions.
|
20
|
+
|
21
|
+
This became extremly useful with [Dynamic Programming](https://en.wikipedia.org/wiki/Dynamic_programming#Dijkstra.27s_algorithm_for_the_shortest_path_problem) .
|
22
|
+
|
23
|
+
A much more simle example is allowing us to express and implement the [Fibonacci Sequence](https://en.wikipedia.org/wiki/Dynamic_programming#Fibonacci_sequence) in the same, some might say naïve, way.
|
24
|
+
|
25
|
+
Compared to the explicit memoization as shown in the Wikipedia article, which would read as follows in Ruby
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
def fibo n, cache=[0, 1]
|
29
|
+
return cache[n] if cache[n]
|
30
|
+
cache[n] = fibo( n.pred, cache ) + fibo( n.pred.pred, cache )
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
It is still amazing how the specialized cache initialisation allows us to get rid of the original if statement.
|
35
|
+
|
36
|
+
However the general case would read like this
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
def f n, cache = {}
|
40
|
+
args_hash = some_hash_fn n # n is all args here
|
41
|
+
return cache[args_hash] if cache[args_hash]
|
42
|
+
cache[args_hash] = f_implemenetation( some_fn(n), cache )
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
While a memoization mechanisme built into the language allos to write things like
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
def_memoized f *args
|
50
|
+
...
|
51
|
+
end
|
52
|
+
|
53
|
+
def f *args
|
54
|
+
...
|
55
|
+
end
|
56
|
+
memoize :f
|
57
|
+
|
58
|
+
# Which can be written as
|
59
|
+
memoize \
|
60
|
+
def f *args
|
61
|
+
...
|
62
|
+
end
|
63
|
+
|
64
|
+
memoized do
|
65
|
+
def f *args
|
66
|
+
...
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
This gem opts for the `memoize` method in the `Module` class as this allows for
|
72
|
+
two different syntaxes
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
memoize def f ...
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
def f ...
|
80
|
+
end
|
81
|
+
memoize :f
|
82
|
+
|
83
|
+
```
|
84
|
+
|
85
|
+
#### Lazy Attributes
|
86
|
+
|
87
|
+
Are just parameterless memoized methods, excatly the same as `let` bindings in [RSpec](http://www.rubydoc.info/gems/rspec-core/RSpec/Core/MemoizedHelpers/ClassMethods#let-instance_method).
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
lazy_attr( :config ){ YAML.read config_file }
|
91
|
+
```
|
92
|
+
|
93
|
+
One could say they are just syntactic sugar for
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
memoize def config
|
97
|
+
YAML.read config_file
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
One would be correct, but lazy attributes are many (in some of my modules and classes) and have a semantic role often very similar to
|
102
|
+
the example above. They are by nature static while methods like the shortest path or fibonacci are highly dynamic.
|
103
|
+
|
104
|
+
For details see the corresponding [QED demo](https://github.com/RobertDober/lab42_core/blob/master/demo/memoization.md).
|
105
|
+
|
106
|
+
#### Gotchas
|
107
|
+
|
108
|
+
Do not, I repeat, **Do not** memoize methods with side effects!
|
109
|
+
|
110
|
+
The exception is _cached reading_ as in the example above.
|
111
|
+
|
112
|
+
Do not call memoized methods with arguments that cannot be used as Hash keys like e.g. BasicObject instances or other objects not responding to the **original** `hash` method.
|
113
|
+
|
14
114
|
## Array
|
15
115
|
|
16
116
|
Can be used after `require 'lab42/core'` or `require 'lab42/core/array'`
|
@@ -91,10 +191,23 @@ For details see the corresponding [QED demo](https://github.com/RobertDober/lab4
|
|
91
191
|
|
92
192
|
## Hash
|
93
193
|
|
194
|
+
### #only
|
195
|
+
|
94
196
|
```ruby
|
95
197
|
{a: 42, b: 43}.only :a, :c # ===> {a: 42}
|
96
198
|
```
|
97
199
|
|
200
|
+
### #fetch! (read fetch and set)
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
a = {a: 42}
|
204
|
+
a.fetch!(b, 43) # or a.fetch!(b){43}
|
205
|
+
a == {a: 42, b: 43 } # true
|
206
|
+
```
|
207
|
+
|
208
|
+
**N.B.** Unlike `Hash#fetch` `Hash#fetch!` will **not** warn you that the block superseeds the default arg if both
|
209
|
+
are provided (after all there is a !).
|
210
|
+
|
98
211
|
For details see the corresponding [QED demo](https://github.com/RobertDober/lab42_core/blob/master/demo/hash.md).
|
99
212
|
|
100
213
|
## Fn
|
data/lib/lab42/core/hash.rb
CHANGED
@@ -16,4 +16,12 @@ class Hash
|
|
16
16
|
default
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
def fetch! key, *defaults, &defblk
|
21
|
+
default_present = !(defaults.empty? && defblk.nil?)
|
22
|
+
return fetch key unless default_present
|
23
|
+
fetch key do
|
24
|
+
self[ key ] = defblk ? defblk.() : defaults.first
|
25
|
+
end
|
26
|
+
end
|
19
27
|
end # class Hash
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'hash'
|
2
|
+
class Module
|
3
|
+
def memoize sym
|
4
|
+
orig_method = instance_method sym
|
5
|
+
define_method sym do |*args|
|
6
|
+
ivar_name = "@__#{sym}__"
|
7
|
+
instance_variable_set ivar_name, {} unless instance_variable_defined? ivar_name
|
8
|
+
instance_variable_get( ivar_name ).fetch! args do
|
9
|
+
# Not cached yet!!!
|
10
|
+
orig_method.bind( self ).( *args )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/lab42/core/version.rb
CHANGED
data/lib/lab42/core.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lab42_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Dober
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: forwarder2
|
@@ -126,6 +126,7 @@ files:
|
|
126
126
|
- lib/lab42/core/file.rb
|
127
127
|
- lib/lab42/core/fn.rb
|
128
128
|
- lib/lab42/core/hash.rb
|
129
|
+
- lib/lab42/core/memoization.rb
|
129
130
|
- lib/lab42/core/meta.rb
|
130
131
|
- lib/lab42/core/old_ruby2.rb
|
131
132
|
- lib/lab42/core/open_object.rb
|