lab42_core 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 16682f25db174dc49c05db95880ad4dbdffb08a6
4
- data.tar.gz: 9303958cffdf2f045054a1f3860de6f509213b43
3
+ metadata.gz: 50b79fb53cb3eabf40d9a75db01d945d338b5dfc
4
+ data.tar.gz: 60df674bd40c480f1dbfd62ac5c11b0a3bf5a7ba
5
5
  SHA512:
6
- metadata.gz: 4cdfb75fd68d03b62a023b6064c5e63d52ea69392e514df65eaaaa8a2ee2f90a4c18479b7db795013ec9c5d0b7d83c6cefdeab117561e2cf89f2105b5bdf6fe7
7
- data.tar.gz: b814d0a775141a045f84b810d7cc37d8cf5159c39e73eb42886a7b74cf6aaf9adcff8b8816302d791ca0b794071ac8b13feebc2dd6cc488c3ed5653de1d8c82a
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Lab42
2
2
  module Core
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.0"
4
4
  end # module Core
5
5
  end # module Lab42
data/lib/lab42/core.rb CHANGED
@@ -7,3 +7,4 @@ require_relative './core/enumerable'
7
7
  require_relative './core/file'
8
8
  require_relative './core/hash'
9
9
  require_relative './core/open_object'
10
+ require_relative './core/memoization'
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.2.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-06 00:00:00.000000000 Z
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