egison 0.6.0 → 1.0.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 +43 -28
- data/lib/egison/core.rb +37 -4
- data/lib/egison/matcher-core.rb +34 -0
- data/lib/egison/version.rb +1 -1
- data/sample/greet.rb +6 -6
- data/sample/primes.rb +10 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83b4172902ecda7ac29219caf4ab1fa2ecdd25df
|
4
|
+
data.tar.gz: 715dd8a2affee299c7d139f07f11e57357bb0082
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7750ee40919a1d41a29fcd7e9520b2d98501e1c87d3df960a5a34c770faff22e91961ba46bdaf323e6a9248b080dcdf083ee04b0938aacc3f72d6a121597591
|
7
|
+
data.tar.gz: 1475df510e8428b4d24ef9225a53e19efe4dd5747f556a7d0d9ae15a7ba61ef030e2ad3b89d4429eb6b67ff6b32ce1948c8f5c10795f21d131f281ab46b472ee
|
data/README.md
CHANGED
@@ -5,13 +5,13 @@ We can directly express pattern-matching against lists, multisets, and sets usin
|
|
5
5
|
|
6
6
|
## Installation
|
7
7
|
|
8
|
-
```
|
8
|
+
```shell
|
9
9
|
$ gem install egison
|
10
10
|
```
|
11
11
|
|
12
12
|
or
|
13
13
|
|
14
|
-
```
|
14
|
+
```shell
|
15
15
|
$ git clone https://github.com/egison/egison-ruby.git
|
16
16
|
$ cd egison-ruby
|
17
17
|
$ make
|
@@ -19,7 +19,7 @@ $ make
|
|
19
19
|
|
20
20
|
or
|
21
21
|
|
22
|
-
```
|
22
|
+
```shell
|
23
23
|
$ gem install bundler (if you need)
|
24
24
|
$ echo "gem 'egison', :git => 'https://github.com/egison/egison-ruby.git'" > Gemfile
|
25
25
|
$ bundle install
|
@@ -29,7 +29,7 @@ $ bundle install
|
|
29
29
|
|
30
30
|
The library provides `Egison#match_all` and `Egison#match`.
|
31
31
|
|
32
|
-
```
|
32
|
+
```Ruby
|
33
33
|
require 'egison'
|
34
34
|
|
35
35
|
include Egison
|
@@ -63,7 +63,7 @@ If a pattern matches, it calls the block passed to the matched match-clause and
|
|
63
63
|
|
64
64
|
## Patterns
|
65
65
|
|
66
|
-
### Element
|
66
|
+
### Element Patterns and Subcollection Patterns
|
67
67
|
|
68
68
|
An <i>element pattern</i> matches the element of the target array.
|
69
69
|
|
@@ -73,7 +73,7 @@ A subcollection pattern has `*` ahead.
|
|
73
73
|
A literal that contain `_` ahead is a <i>pattern-variable</i>.
|
74
74
|
We can refer the result of pattern-matching through them.
|
75
75
|
|
76
|
-
```
|
76
|
+
```Ruby
|
77
77
|
match_all([1, 2, 3]) do
|
78
78
|
with(List.(*_hs, _x, *_ts)) do
|
79
79
|
[hs, x, ts]
|
@@ -81,7 +81,7 @@ match_all([1, 2, 3]) do
|
|
81
81
|
end #=> [[[],1,[2,3]],[[1],2,[3]],[[1,2],3,[]]
|
82
82
|
```
|
83
83
|
|
84
|
-
### Three
|
84
|
+
### Three Matchers: List, Multiset, Set
|
85
85
|
|
86
86
|
We can write pattern-matching against lists, multisets, and sets.
|
87
87
|
When we regard an array as a multiset, the order of elements is ignored.
|
@@ -92,7 +92,7 @@ It matches with any object.
|
|
92
92
|
Note that `__` and `___` are also interpreted as a wildcard.
|
93
93
|
This is because `_` and `__` are system variables and sometimes have its own meaning.
|
94
94
|
|
95
|
-
```
|
95
|
+
```Ruby
|
96
96
|
match_all([1, 2, 3]) do
|
97
97
|
with(List.(_a, _b, *_)) do
|
98
98
|
[a, b]
|
@@ -114,7 +114,7 @@ end #=> [[1, 1],[1, 2],[1, 3],[2, 1],[2, 2],[2, 3],[3, 1],[3, 2],[3, 3]]
|
|
114
114
|
|
115
115
|
Note that `_[]` is provided as syntactic sugar for `List.()`.
|
116
116
|
|
117
|
-
```
|
117
|
+
```Ruby
|
118
118
|
match_all([1, 2, 3]) do
|
119
119
|
with(_[_a, _b, *_]) do
|
120
120
|
[a, b]
|
@@ -122,7 +122,7 @@ match_all([1, 2, 3]) do
|
|
122
122
|
end #=> [[1, 2]]
|
123
123
|
```
|
124
124
|
|
125
|
-
### Non-
|
125
|
+
### Non-Linear Patterns
|
126
126
|
|
127
127
|
Non-linear pattern is the most important feature of our pattern-matching system.
|
128
128
|
Our pattern-matching system allows users multiple occurrences of same variables in a pattern.
|
@@ -130,7 +130,7 @@ A Pattern whose form is `__("...")` is a value pattern.
|
|
130
130
|
In the place of `...`, we can write any ruby expression we like.
|
131
131
|
It matches the target when the target is equal with the value that `...` evaluated to.
|
132
132
|
|
133
|
-
```
|
133
|
+
```Ruby
|
134
134
|
match_all([5, 3, 4, 1, 2]) do
|
135
135
|
with(Multiset.(_a, __("a + 1"), __("a + 2"), *_)) do
|
136
136
|
a
|
@@ -140,7 +140,7 @@ end #=> [1,2,3]
|
|
140
140
|
|
141
141
|
When, the expression in the place of `...` is a single variable, we can omit `("` and `")` as follow.
|
142
142
|
|
143
|
-
```
|
143
|
+
```Ruby
|
144
144
|
match_all([1, 2, 3, 2, 5]) do
|
145
145
|
with(Multiset.(_a, __a, *_)) do
|
146
146
|
a
|
@@ -152,7 +152,7 @@ end #=> [2,2]
|
|
152
152
|
|
153
153
|
We can do pattern-matching against streams with the `match_stream` expression.
|
154
154
|
|
155
|
-
```
|
155
|
+
```Ruby
|
156
156
|
def nats
|
157
157
|
(1..Float::INFINITY)
|
158
158
|
end
|
@@ -170,7 +170,7 @@ match_stream(nats){ with(Set.(_m, _n, *_)) { [m, n] } }.take(10)
|
|
170
170
|
|
171
171
|
We can enumerates all combinations of the elements of a collection with pattern-matching.
|
172
172
|
|
173
|
-
```
|
173
|
+
```Ruby
|
174
174
|
require 'egison'
|
175
175
|
|
176
176
|
include Egison
|
@@ -189,7 +189,7 @@ We can write patterns for all poker-hands in one single pattern.
|
|
189
189
|
It is as follow.
|
190
190
|
Isn't it exciting?
|
191
191
|
|
192
|
-
```
|
192
|
+
```Ruby
|
193
193
|
require 'egison'
|
194
194
|
|
195
195
|
include Egison
|
@@ -232,20 +232,20 @@ p(poker_hands([["diamond", 4], ["club", 2], ["club", 5], ["heart", 1], ["diamond
|
|
232
232
|
p(poker_hands([["diamond", 4], ["club", 10], ["club", 5], ["heart", 1], ["diamond", 3]])) #=> "Nothing"
|
233
233
|
```
|
234
234
|
|
235
|
-
### Twin Primes
|
235
|
+
### Twin Primes and Prime Triplets
|
236
236
|
|
237
237
|
The following code enumerates all twin primes with pattern-matching!
|
238
238
|
I believe it is also a really exciting demonstration.
|
239
239
|
|
240
|
-
```
|
240
|
+
```Ruby
|
241
241
|
require 'egison'
|
242
242
|
require 'prime'
|
243
243
|
|
244
244
|
include Egison
|
245
245
|
|
246
246
|
twin_primes = match_stream(Prime) {
|
247
|
-
with(List.(*_,
|
248
|
-
[
|
247
|
+
with(List.(*_, _p, __("p + 2"), *_)) {
|
248
|
+
[p, p + 2]
|
249
249
|
}
|
250
250
|
}
|
251
251
|
|
@@ -253,22 +253,35 @@ p twin_primes.take(10)
|
|
253
253
|
#=>[[3, 5], [5, 7], [11, 13], [17, 19], [29, 31], [41, 43], [59, 61], [71, 73], [101, 103], [107, 109]]
|
254
254
|
```
|
255
255
|
|
256
|
+
We can also enumerate prime triplets using **and-patterns** and **or-patterns** effectively.
|
257
|
+
|
258
|
+
```Ruby
|
259
|
+
prime_triplets = match_stream(Prime) {
|
260
|
+
with(List.(*_, _p, And(Or(__("p + 2"), __("p + 4")), _m), __("p + 6"), *_)) {
|
261
|
+
[p, m, p + 6]
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
p prime_triplets.take(10)
|
266
|
+
#=>[[5, 7, 11], [7, 11, 13], [11, 13, 17], [13, 17, 19], [17, 19, 23], [37, 41, 43], [41, 43, 47], [67, 71, 73], [97, 101, 103], [101, 103, 107]]
|
267
|
+
```
|
268
|
+
|
256
269
|
### Algebraic Data Types
|
257
270
|
|
258
271
|
We can also patten match against algebraic data types as ordinary functional programming languages.
|
259
272
|
Here is a simple example.
|
260
273
|
Note that, the object in the pattern matches if the target object is equal with it.
|
261
274
|
|
262
|
-
```
|
275
|
+
```Ruby
|
263
276
|
class User < Struct.new(:name, :gender, :married, :doctor, :professor)
|
264
277
|
def greet
|
265
278
|
match(self) do
|
266
|
-
with(
|
267
|
-
with(
|
268
|
-
with(
|
269
|
-
with(
|
270
|
-
with(
|
271
|
-
with(
|
279
|
+
with(User.(_name, _, _, _, true)) { "Hello, Prof. #{name}!" }
|
280
|
+
with(User.(_name, _, _, true, _)) { "Hello, Dr. #{name}!" }
|
281
|
+
with(User.(_name, :female, true, _, _)) { "Hello, Mrs. #{name}!" }
|
282
|
+
with(User.(_name, :female, _, _, _)) { "Hello, Ms. #{name}!" }
|
283
|
+
with(User.(_name, :male, _, _, _)) { "Hello, Mr. #{name}!" }
|
284
|
+
with(User.(_name, _, _, _, _)) { "Hello, #{name}!" }
|
272
285
|
end
|
273
286
|
end
|
274
287
|
end
|
@@ -302,12 +315,14 @@ There is a new programming world!
|
|
302
315
|
|
303
316
|
## Contact
|
304
317
|
|
305
|
-
If you get interested in this Gem, please contact
|
318
|
+
If you get interested in this Gem, please contact <a target="_blank" href="http://www.egison.org/~egi/">Satoshi Egi</a> or tweet to <a target="_blank" href="https://twitter.com/Egison_Lang">@Egison_Lang</a>.
|
319
|
+
|
320
|
+
We will talk about this gem in <a target="_blank" href="http://rubykaigi.org/2014">RubyKaigi 2014</a>!
|
306
321
|
|
307
322
|
## LICENSE
|
308
323
|
|
309
324
|
The license of this library code is BSD.
|
310
|
-
I learned how to extend Ruby and how to write a gem from the code of
|
325
|
+
I learned how to extend Ruby and how to write a gem from the code of <a target="_blank" href="https://github.com/k-tsj/pattern-match">the pattern-match gem</a> by Kazuki Tsujimoto.
|
311
326
|
I designed syntax of pattern-matching to go with that gem.
|
312
327
|
This library contains the copy from that gem.
|
313
328
|
The full license text is [here](https://github.com/egisatoshi/egison-ruby/blob/master/LICENSE).
|
data/lib/egison/core.rb
CHANGED
@@ -141,10 +141,9 @@ module PatternMatch
|
|
141
141
|
def match(tgt, bindings)
|
142
142
|
tgt = tgt.to_a
|
143
143
|
if subpatterns.empty?
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
return []
|
144
|
+
unnileds = @matcher.unnil(tgt)
|
145
|
+
unnileds.map do
|
146
|
+
[[], []]
|
148
147
|
end
|
149
148
|
else
|
150
149
|
subpatterns = @subpatterns.clone
|
@@ -219,6 +218,32 @@ module PatternMatch
|
|
219
218
|
end
|
220
219
|
end
|
221
220
|
|
221
|
+
class OrPattern < PatternElement
|
222
|
+
attr_reader :pats
|
223
|
+
|
224
|
+
def initialize(*subpatterns)
|
225
|
+
super()
|
226
|
+
@pats = subpatterns
|
227
|
+
end
|
228
|
+
|
229
|
+
def match(tgt, bindings)
|
230
|
+
@pats.map { |pat| [[[pat, tgt]], []] }
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
class AndPattern < PatternElement
|
235
|
+
attr_reader :pats
|
236
|
+
|
237
|
+
def initialize(*subpatterns)
|
238
|
+
super()
|
239
|
+
@pats = subpatterns
|
240
|
+
end
|
241
|
+
|
242
|
+
def match(tgt, bindings)
|
243
|
+
[[@pats.map { |pat| [pat, tgt] }, []]]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
222
247
|
class Wildcard < PatternElement
|
223
248
|
def initialize()
|
224
249
|
super()
|
@@ -375,6 +400,14 @@ module PatternMatch
|
|
375
400
|
end
|
376
401
|
end
|
377
402
|
|
403
|
+
def Or(*subpatterns)
|
404
|
+
OrPattern.new(*subpatterns)
|
405
|
+
end
|
406
|
+
|
407
|
+
def And(*subpatterns)
|
408
|
+
AndPattern.new(*subpatterns)
|
409
|
+
end
|
410
|
+
|
378
411
|
class BindingModule < ::Module
|
379
412
|
end
|
380
413
|
|
data/lib/egison/matcher-core.rb
CHANGED
@@ -4,6 +4,14 @@ require 'egison/lazyarray'
|
|
4
4
|
class Class
|
5
5
|
include PatternMatch::Matchable
|
6
6
|
|
7
|
+
def unnil(val)
|
8
|
+
if val.empty?
|
9
|
+
[[]]
|
10
|
+
else
|
11
|
+
[]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
7
15
|
def uncons(val)
|
8
16
|
raise NotImplementedError, "need to define `#{__method__}'"
|
9
17
|
end
|
@@ -23,6 +31,32 @@ end
|
|
23
31
|
module Egison
|
24
32
|
extend self
|
25
33
|
|
34
|
+
class << Struct
|
35
|
+
def unnil(val)
|
36
|
+
[[]]
|
37
|
+
end
|
38
|
+
|
39
|
+
def uncons(val)
|
40
|
+
val2 = val.clone
|
41
|
+
x = val2.shift
|
42
|
+
[[x, val2]]
|
43
|
+
end
|
44
|
+
|
45
|
+
def unjoin(val)
|
46
|
+
val2 = val.clone
|
47
|
+
xs = []
|
48
|
+
ys = val2.clone
|
49
|
+
rets = [[xs, ys]]
|
50
|
+
until val2.empty? do
|
51
|
+
x = val2.shift
|
52
|
+
ys = val2.clone
|
53
|
+
xs += [x]
|
54
|
+
rets += [[xs, ys]]
|
55
|
+
end
|
56
|
+
rets
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
26
60
|
class List
|
27
61
|
end
|
28
62
|
|
data/lib/egison/version.rb
CHANGED
data/sample/greet.rb
CHANGED
@@ -5,12 +5,12 @@ include Egison
|
|
5
5
|
class User < Struct.new(:name, :gender, :married, :doctor, :professor)
|
6
6
|
def greet
|
7
7
|
match(self) do
|
8
|
-
with(
|
9
|
-
with(
|
10
|
-
with(
|
11
|
-
with(
|
12
|
-
with(
|
13
|
-
with(
|
8
|
+
with(User.(_name, _, _, _, true)) { "Hello, Prof. #{name}!" }
|
9
|
+
with(User.(_name, _, _, true)) { "Hello, Dr. #{name}!" }
|
10
|
+
with(User.(_name, :female, true)) { "Hello, Mrs. #{name}!" }
|
11
|
+
with(User.(_name, :female)) { "Hello, Ms. #{name}!" }
|
12
|
+
with(User.(_name, :male)) { "Hello, Mr. #{name}!" }
|
13
|
+
with(User.(_name)) { "Hello, #{name}!" }
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
data/sample/primes.rb
CHANGED
@@ -4,9 +4,17 @@ require 'prime'
|
|
4
4
|
include Egison
|
5
5
|
|
6
6
|
twin_primes = match_stream(Prime) {
|
7
|
-
with(List.(*_,
|
8
|
-
[
|
7
|
+
with(List.(*_, _p, __("p + 2"), *_)) {
|
8
|
+
[p, p + 2]
|
9
9
|
}
|
10
10
|
}
|
11
11
|
|
12
12
|
p twin_primes.take(10)
|
13
|
+
|
14
|
+
prime_triplets = match_stream(Prime) {
|
15
|
+
with(List.(*_, _p, And(Or(__("p + 2"), __("p + 4")), _m), __("p + 6"), *_)) {
|
16
|
+
[p, m, p + 6]
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
p prime_triplets.take(10)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: egison
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Satoshi Egi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|