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 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