rupture 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rbenv-version +1 -0
- data/Gemfile +3 -0
- data/README.md +4 -1
- data/Rakefile +2 -28
- data/VERSION +1 -1
- data/lib/rupture/array_seq.rb +0 -1
- data/lib/rupture/cons.rb +1 -1
- data/lib/rupture/core_ext.rb +14 -0
- data/lib/rupture/fn.rb +31 -5
- data/lib/rupture/function.rb +77 -17
- data/lib/rupture/hash_map.rb +47 -0
- data/lib/rupture/lazy_seq.rb +2 -3
- data/lib/rupture/list.rb +3 -5
- data/lib/rupture/map.rb +38 -0
- data/lib/rupture/meta.rb +1 -1
- data/lib/rupture/rails_ext.rb +20 -0
- data/lib/rupture/reader.rb +11 -11
- data/lib/rupture/seq.rb +15 -20
- data/lib/rupture/sequence.rb +57 -16
- data/lib/rupture.rb +2 -0
- data/rupture.gemspec +18 -59
- data/test/fn_test.rb +5 -0
- data/test/function_test.rb +66 -0
- data/test/map_test.rb +37 -0
- data/test/seq_test.rb +22 -4
- data/test/test_helper.rb +1 -1
- metadata +86 -16
- data/init.rb +0 -1
data/.gitignore
ADDED
data/.rbenv-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ree-1.8.7-2012.02
|
data/Gemfile
ADDED
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -1,36 +1,10 @@
|
|
1
|
-
require 'rake'
|
2
1
|
require 'rake/testtask'
|
3
|
-
require '
|
4
|
-
|
5
|
-
begin
|
6
|
-
require 'jeweler'
|
7
|
-
Jeweler::Tasks.new do |s|
|
8
|
-
s.name = "rupture"
|
9
|
-
s.summary = %Q{Clojure sequence functions for Ruby.}
|
10
|
-
s.email = "code@justinbalthrop.com"
|
11
|
-
s.homepage = "http://github.com/flatland/rupture"
|
12
|
-
s.description = "Clojure sequence functions for Ruby."
|
13
|
-
s.authors = ["Justin Balthrop"]
|
14
|
-
end
|
15
|
-
Jeweler::GemcutterTasks.new
|
16
|
-
rescue LoadError
|
17
|
-
puts "Jeweler not available. Install it with: sudo gem install jeweler"
|
18
|
-
end
|
2
|
+
require 'bundler/gem_tasks'
|
19
3
|
|
20
4
|
Rake::TestTask.new do |t|
|
21
|
-
t.libs
|
5
|
+
t.libs = ['lib']
|
22
6
|
t.pattern = 'test/**/*_test.rb'
|
23
7
|
t.verbose = false
|
24
8
|
end
|
25
9
|
|
26
|
-
begin
|
27
|
-
require 'rcov/rcovtask'
|
28
|
-
Rcov::RcovTask.new do |t|
|
29
|
-
t.libs << 'test'
|
30
|
-
t.test_files = FileList['test/**/*_test.rb']
|
31
|
-
t.verbose = true
|
32
|
-
end
|
33
|
-
rescue LoadError
|
34
|
-
end
|
35
|
-
|
36
10
|
task :default => :test
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/rupture/array_seq.rb
CHANGED
data/lib/rupture/cons.rb
CHANGED
data/lib/rupture/core_ext.rb
CHANGED
data/lib/rupture/fn.rb
CHANGED
@@ -17,8 +17,28 @@ module Rupture
|
|
17
17
|
alias * comp
|
18
18
|
|
19
19
|
def partial(*partials)
|
20
|
+
lambda do |*args, &block|
|
21
|
+
call(*(partials + args), &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def rpartial(*partials)
|
26
|
+
lambda do |*args, &block|
|
27
|
+
call(*(args + partials), &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def <<(partials)
|
32
|
+
partial(*partials)
|
33
|
+
end
|
34
|
+
|
35
|
+
def >>(partials)
|
36
|
+
rpartial(*partials)
|
37
|
+
end
|
38
|
+
|
39
|
+
def block(&block)
|
20
40
|
lambda do |*args|
|
21
|
-
call(*
|
41
|
+
call(*args, &block)
|
22
42
|
end
|
23
43
|
end
|
24
44
|
|
@@ -27,9 +47,15 @@ module Rupture
|
|
27
47
|
# call(*F.concat(args, last))
|
28
48
|
# end
|
29
49
|
|
50
|
+
def fnil(*defaults)
|
51
|
+
lambda do |*args|
|
52
|
+
call(*F.map(args, defaults) {|a,d| a.nil? ? d : a})
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
30
56
|
def to_proc
|
31
57
|
lambda do |key|
|
32
|
-
|
58
|
+
call(key)
|
33
59
|
end
|
34
60
|
end
|
35
61
|
|
@@ -50,8 +76,8 @@ end
|
|
50
76
|
class Symbol
|
51
77
|
include Rupture::Fn
|
52
78
|
|
53
|
-
def call(object
|
54
|
-
object.method(self)
|
79
|
+
def call(object, *args, &block)
|
80
|
+
object.method(self).call(*args, &block)
|
55
81
|
end
|
56
82
|
alias [] call
|
57
83
|
|
@@ -82,7 +108,7 @@ end
|
|
82
108
|
class Module
|
83
109
|
def [](method_name, *partials)
|
84
110
|
lambda do |*args|
|
85
|
-
self.send(method_name, *(
|
111
|
+
self.send(method_name, *(partials + args))
|
86
112
|
end
|
87
113
|
end
|
88
114
|
end
|
data/lib/rupture/function.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
module Rupture
|
2
2
|
module Function
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
firsts = seqs.collect(&:first)
|
11
|
-
rests = seqs.collect(&:rest)
|
12
|
-
cons(f[*firsts], map(*rests, &f))
|
3
|
+
def map(*colls, &fn)
|
4
|
+
fn ||= colls.shift
|
5
|
+
lazy_loop(colls.seq.map(:seq)) do |recur, seqs|
|
6
|
+
if seqs.every?
|
7
|
+
firsts = seqs.map(:first)
|
8
|
+
nexts = seqs.map(:next)
|
9
|
+
cons(fn[*firsts], recur[nexts])
|
13
10
|
end
|
14
11
|
end
|
15
12
|
end
|
@@ -25,9 +22,9 @@ module Rupture
|
|
25
22
|
end
|
26
23
|
end
|
27
24
|
|
28
|
-
def mapcat(*colls, &
|
29
|
-
|
30
|
-
concat(*map(*colls, &
|
25
|
+
def mapcat(*colls, &fn)
|
26
|
+
fn ||= colls.shift
|
27
|
+
concat(*map(*colls, &fn))
|
31
28
|
end
|
32
29
|
|
33
30
|
def zip(*colls)
|
@@ -41,6 +38,20 @@ module Rupture
|
|
41
38
|
end
|
42
39
|
end
|
43
40
|
|
41
|
+
def filter(pred, coll)
|
42
|
+
lazy_seq do
|
43
|
+
if s = coll.seq
|
44
|
+
e = s.first
|
45
|
+
tail = filter(pred, s.rest)
|
46
|
+
pred[e] ? cons(e, tail) : tail
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove(pred, coll)
|
52
|
+
filter(pred.complement, coll)
|
53
|
+
end
|
54
|
+
|
44
55
|
def loop(*vals)
|
45
56
|
more = true
|
46
57
|
recur = lambda {|*vals| more = true}
|
@@ -52,12 +63,19 @@ module Rupture
|
|
52
63
|
result
|
53
64
|
end
|
54
65
|
|
55
|
-
def
|
56
|
-
|
66
|
+
def lazy_loop(*vals, &block)
|
67
|
+
recur = lambda do |*v|
|
68
|
+
lazy_seq {block[recur, *v]}
|
69
|
+
end
|
70
|
+
recur[*vals]
|
71
|
+
end
|
72
|
+
|
73
|
+
def iterate(*args, &fn)
|
74
|
+
fn ||= args.shift
|
57
75
|
Utils.verify_args(args, 1)
|
58
76
|
x = args.first
|
59
77
|
lazy_seq do
|
60
|
-
cons(x, iterate(
|
78
|
+
cons(x, iterate(fn[x], &fn))
|
61
79
|
end
|
62
80
|
end
|
63
81
|
|
@@ -83,7 +101,7 @@ module Rupture
|
|
83
101
|
|
84
102
|
def lazy_seq(f = nil, &fn)
|
85
103
|
fn ||= f
|
86
|
-
LazySeq.new(
|
104
|
+
LazySeq.new(fn)
|
87
105
|
end
|
88
106
|
|
89
107
|
def cons(head, tail)
|
@@ -94,6 +112,19 @@ module Rupture
|
|
94
112
|
List.new(*xs)
|
95
113
|
end
|
96
114
|
|
115
|
+
def hash_map(*kvs, &block)
|
116
|
+
h = block_given? ? HashMap.new(&block) : HashMap.empty
|
117
|
+
if kvs.size == 1
|
118
|
+
h.into(kvs.first)
|
119
|
+
else
|
120
|
+
h.assoc(*kvs)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def constantly(x)
|
125
|
+
lambda {x}
|
126
|
+
end
|
127
|
+
|
97
128
|
def identity(x)
|
98
129
|
x
|
99
130
|
end
|
@@ -108,6 +139,35 @@ module Rupture
|
|
108
139
|
juxt(identity, *args)
|
109
140
|
end
|
110
141
|
|
142
|
+
def let(*vals)
|
143
|
+
yield(*vals)
|
144
|
+
end
|
145
|
+
|
146
|
+
def fix(val, pred, f = nil, &fn)
|
147
|
+
fn ||= f
|
148
|
+
pred = pred.to_proc[val] if pred.respond_to?(:to_proc)
|
149
|
+
pred ? fn[val] : val
|
150
|
+
end
|
151
|
+
|
152
|
+
def when_let(val)
|
153
|
+
yield(val) if val
|
154
|
+
end
|
155
|
+
|
156
|
+
def for(s, *seqs, &fn)
|
157
|
+
if seqs.empty?
|
158
|
+
s.seq.map(&fn)
|
159
|
+
else
|
160
|
+
lazy_seq do
|
161
|
+
tails = self.for(*seqs) {|*args| args}
|
162
|
+
s.seq.mapcat do |head|
|
163
|
+
tails.map do |tail|
|
164
|
+
fn[head, *tail]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
111
171
|
extend Function
|
112
172
|
F = Function
|
113
173
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'hamster'
|
2
|
+
|
3
|
+
module Rupture
|
4
|
+
class HashMap < Hamster::Hash
|
5
|
+
include Map
|
6
|
+
|
7
|
+
def as_map
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.empty
|
12
|
+
@empty ||= HashMap.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Hash
|
18
|
+
include Rupture::Map
|
19
|
+
|
20
|
+
def as_map
|
21
|
+
F.hash_map(self)
|
22
|
+
end
|
23
|
+
alias ~ as_map
|
24
|
+
|
25
|
+
def assoc!(*vals)
|
26
|
+
vals.each_slice(2) do |k,v|
|
27
|
+
self[k] = v
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def update!(key, fn = nil, *args, &block)
|
33
|
+
self[key] = if fn
|
34
|
+
fn.call(self[key], *args, &block)
|
35
|
+
else
|
36
|
+
yield(self[key])
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def update_each!(keys, *args, &block)
|
42
|
+
keys.each do |key|
|
43
|
+
update!(key, *args, &block)
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
end
|
data/lib/rupture/lazy_seq.rb
CHANGED
data/lib/rupture/list.rb
CHANGED
@@ -6,17 +6,13 @@ module Rupture
|
|
6
6
|
private_class_method :create
|
7
7
|
attr_reader :seq, :size
|
8
8
|
|
9
|
-
def self.empty
|
10
|
-
@empty ||= create(nil, 0)
|
11
|
-
end
|
12
|
-
|
13
9
|
def initialize(seq, size)
|
14
10
|
@seq = seq.seq
|
15
11
|
@size = size
|
16
12
|
end
|
17
13
|
|
18
14
|
def self.new(*args)
|
19
|
-
list =
|
15
|
+
list = Empty
|
20
16
|
args.reverse_each do |x|
|
21
17
|
list = list.conj(x)
|
22
18
|
end
|
@@ -27,5 +23,7 @@ module Rupture
|
|
27
23
|
def conj(x)
|
28
24
|
self.class.send(:create, Cons.new(x, @seq), @size.inc)
|
29
25
|
end
|
26
|
+
|
27
|
+
Empty = create(nil, 0)
|
30
28
|
end
|
31
29
|
end
|
data/lib/rupture/map.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Rupture
|
2
|
+
module Map
|
3
|
+
def map?
|
4
|
+
true
|
5
|
+
end
|
6
|
+
|
7
|
+
def assoc(*kvs)
|
8
|
+
into(kvs.seq.partition(2))
|
9
|
+
end
|
10
|
+
|
11
|
+
def into(other)
|
12
|
+
other.seq.reduce(as_map) do |map, (k,v)|
|
13
|
+
map.put(k,v)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def update(key, fn = nil, *args, &block)
|
18
|
+
if fn
|
19
|
+
assoc(key, fn.call(self[key], *args, &block))
|
20
|
+
else
|
21
|
+
assoc(key, yield(self[key]))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_each(keys, *args, &block)
|
26
|
+
map = as_map
|
27
|
+
keys.each do |key|
|
28
|
+
update(key, *args, &block)
|
29
|
+
end
|
30
|
+
map
|
31
|
+
end
|
32
|
+
|
33
|
+
def destruct(*keys)
|
34
|
+
vals = keys.seq.map(self)
|
35
|
+
block_given? ? yield(*vals) : vals
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/rupture/meta.rb
CHANGED
@@ -5,7 +5,7 @@ module Rupture::Meta
|
|
5
5
|
|
6
6
|
def clone(meta = nil)
|
7
7
|
meta ||= @_meta.clone if @_meta
|
8
|
-
raise "meta must be of type Hash, it is #{meta.class}" unless meta.nil? or meta.kind_of?(Hash)
|
8
|
+
raise "meta must be of type Hash, it is #{meta.class}" unless meta.nil? or meta.kind_of?(Hash) or meta.kind_of?(Rupture::HashMap)
|
9
9
|
copy = super()
|
10
10
|
copy.instance_variable_set(:@_meta, meta)
|
11
11
|
copy
|
data/lib/rupture/rails_ext.rb
CHANGED
@@ -36,3 +36,23 @@ class Hash
|
|
36
36
|
dup.stringify_keys!
|
37
37
|
end
|
38
38
|
end
|
39
|
+
|
40
|
+
if defined?(HashWithIndifferentAccess)
|
41
|
+
class HashWithIndifferentAccess
|
42
|
+
def to_hash
|
43
|
+
Hash.new(default).merge(self).with_meta(meta)
|
44
|
+
end
|
45
|
+
|
46
|
+
def symbolize_keys
|
47
|
+
to_hash.symbolize_keys!
|
48
|
+
end
|
49
|
+
|
50
|
+
def deep_symbolize_keys
|
51
|
+
to_hash.deep_symbolize_keys!
|
52
|
+
end
|
53
|
+
|
54
|
+
def stringify_keys
|
55
|
+
to_hash.stringify_keys!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/rupture/reader.rb
CHANGED
@@ -33,22 +33,22 @@ module Rupture
|
|
33
33
|
|
34
34
|
def read
|
35
35
|
case c = getc
|
36
|
-
when '('
|
37
|
-
when '['
|
38
|
-
when '{'
|
39
|
-
when '"'
|
40
|
-
when ':'
|
41
|
-
when ' '
|
42
|
-
when /\d/
|
43
|
-
when /\w/
|
36
|
+
when '(' then read_list
|
37
|
+
when '[' then read_list(Array, ']')
|
38
|
+
when '{' then read_map
|
39
|
+
when '"' then read_string
|
40
|
+
when ':' then read_keyword
|
41
|
+
when ' ' then read
|
42
|
+
when /\d/ then ungetc(c); read_number
|
43
|
+
when /\w/ then ungetc(c); read_symbol
|
44
44
|
when '-'
|
45
45
|
case c = getc
|
46
|
-
when /\d/
|
47
|
-
else
|
46
|
+
when /\d/ then ungetc(c); -read_number
|
47
|
+
else ungetc('-', c); read_symbol
|
48
48
|
end
|
49
49
|
when '#'
|
50
50
|
case c = getc
|
51
|
-
when '{'
|
51
|
+
when '{' then read_list(Set, '}')
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
data/lib/rupture/seq.rb
CHANGED
@@ -1,17 +1,10 @@
|
|
1
1
|
module Rupture
|
2
|
-
class Seq
|
2
|
+
class Seq
|
3
|
+
include Enumerable
|
3
4
|
include Sequence
|
4
5
|
|
5
|
-
def
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
def each
|
10
|
-
s = self
|
11
|
-
while s = s.seq
|
12
|
-
yield s.first
|
13
|
-
s = s.rest
|
14
|
-
end
|
6
|
+
def inspect
|
7
|
+
"(#{to_a.collect(&:inspect).join(',')})"
|
15
8
|
end
|
16
9
|
|
17
10
|
def [](index)
|
@@ -22,7 +15,9 @@ module Rupture
|
|
22
15
|
to_a
|
23
16
|
end
|
24
17
|
|
25
|
-
def
|
18
|
+
def eql?(other)
|
19
|
+
return false unless other.respond_to?(:seq)
|
20
|
+
|
26
21
|
s = self.seq
|
27
22
|
o = other.seq
|
28
23
|
while s && o
|
@@ -30,18 +25,18 @@ module Rupture
|
|
30
25
|
s = s.next
|
31
26
|
o = o.next
|
32
27
|
end
|
28
|
+
|
33
29
|
s.nil? and o.nil?
|
34
30
|
end
|
31
|
+
alias == eql?
|
35
32
|
|
36
|
-
|
37
|
-
|
33
|
+
class EmptySeq < Seq
|
34
|
+
def seq
|
35
|
+
nil
|
36
|
+
end
|
38
37
|
end
|
39
|
-
end
|
40
38
|
|
41
|
-
|
42
|
-
def seq
|
43
|
-
nil
|
44
|
-
end
|
39
|
+
Empty = EmptySeq.new
|
45
40
|
end
|
46
41
|
end
|
47
42
|
|
@@ -57,6 +52,6 @@ class NilClass
|
|
57
52
|
end
|
58
53
|
|
59
54
|
def rest
|
60
|
-
Rupture::Seq
|
55
|
+
Rupture::Seq::Empty
|
61
56
|
end
|
62
57
|
end
|
data/lib/rupture/sequence.rb
CHANGED
@@ -24,6 +24,18 @@ module Rupture
|
|
24
24
|
self if seq
|
25
25
|
end
|
26
26
|
|
27
|
+
def divide
|
28
|
+
[first, rest]
|
29
|
+
end
|
30
|
+
|
31
|
+
def each
|
32
|
+
s = self
|
33
|
+
while s = s.seq
|
34
|
+
yield s.first
|
35
|
+
s = s.rest
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
27
39
|
def count
|
28
40
|
F.loop(0, self) do |recur, i, s|
|
29
41
|
if s.empty?
|
@@ -36,10 +48,6 @@ module Rupture
|
|
36
48
|
end
|
37
49
|
end
|
38
50
|
|
39
|
-
def inspect
|
40
|
-
"(#{to_a.collect(&:inspect).join(' ')})"
|
41
|
-
end
|
42
|
-
|
43
51
|
def conj(item)
|
44
52
|
F.cons(item, self)
|
45
53
|
end
|
@@ -49,7 +57,7 @@ module Rupture
|
|
49
57
|
end
|
50
58
|
|
51
59
|
def reverse
|
52
|
-
Seq.
|
60
|
+
Seq::Empty.into(self)
|
53
61
|
end
|
54
62
|
|
55
63
|
def take(n)
|
@@ -77,7 +85,7 @@ module Rupture
|
|
77
85
|
if lead
|
78
86
|
recur[s.next, lead.next]
|
79
87
|
else
|
80
|
-
s || Seq
|
88
|
+
s || Seq::Empty
|
81
89
|
end
|
82
90
|
end
|
83
91
|
end
|
@@ -124,18 +132,12 @@ module Rupture
|
|
124
132
|
|
125
133
|
def filter(p = nil, &pred)
|
126
134
|
pred ||= p
|
127
|
-
F.
|
128
|
-
if s = seq
|
129
|
-
i = s.first
|
130
|
-
tail = s.rest.filter(pred)
|
131
|
-
pred[i] ? F.cons(i, tail) : tail
|
132
|
-
end
|
133
|
-
end
|
135
|
+
F.filter(pred, self)
|
134
136
|
end
|
135
137
|
|
136
138
|
def remove(p = nil, &pred)
|
137
139
|
pred ||= p
|
138
|
-
|
140
|
+
F.remove(pred, self)
|
139
141
|
end
|
140
142
|
|
141
143
|
def separate(p = nil, &pred)
|
@@ -154,7 +156,16 @@ module Rupture
|
|
154
156
|
|
155
157
|
def map(f = nil, &fn)
|
156
158
|
fn ||= f
|
157
|
-
F.
|
159
|
+
F.lazy_loop(seq) do |recur, s|
|
160
|
+
F.cons(fn[s.first], recur[s.next]) if s
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def map_indexed(f = nil, &fn)
|
165
|
+
fn ||= f
|
166
|
+
F.lazy_loop(0, seq) do |recur, i, s|
|
167
|
+
F.cons(fn[i, s.first], recur[i.inc, s.next]) if s
|
168
|
+
end
|
158
169
|
end
|
159
170
|
|
160
171
|
def concat(*colls)
|
@@ -179,6 +190,19 @@ module Rupture
|
|
179
190
|
end
|
180
191
|
end
|
181
192
|
|
193
|
+
def reductions(*args, &fn)
|
194
|
+
fn ||= args.shift
|
195
|
+
Utils.verify_args(args, 0, 1)
|
196
|
+
acc, coll = (args.empty? ? divide : [args.first, self])
|
197
|
+
|
198
|
+
F.lazy_loop(acc, coll) do |recur, acc, coll|
|
199
|
+
if coll.seq
|
200
|
+
acc = fn[acc, coll.first]
|
201
|
+
F.cons(acc, recur[acc, coll.rest])
|
202
|
+
end
|
203
|
+
end.conj(acc)
|
204
|
+
end
|
205
|
+
|
182
206
|
def foldr(*args, &fn)
|
183
207
|
fn ||= args.shift
|
184
208
|
reverse.reduce(*args) {|a,b| fn[b,a]}
|
@@ -253,12 +277,29 @@ module Rupture
|
|
253
277
|
fn ||= f
|
254
278
|
F.lazy_seq do
|
255
279
|
if s = seq
|
256
|
-
run = F.cons(s.first,
|
280
|
+
run = F.cons(s.first,
|
281
|
+
s.partition(2,1).take_while {|i| not fn[*i]}.map(:second))
|
257
282
|
F.cons(run, s.drop(run.count).partition_between(fn))
|
258
283
|
end
|
259
284
|
end
|
260
285
|
end
|
261
286
|
|
287
|
+
def sort(c = nil, &cmp)
|
288
|
+
cmp||= c
|
289
|
+
super(&cmp).seq
|
290
|
+
end
|
291
|
+
|
292
|
+
def sort_by(f = nil, &fn)
|
293
|
+
fn ||= f
|
294
|
+
sort {|a,b| fn[a] <=> fn[b]}
|
295
|
+
end
|
296
|
+
|
297
|
+
def frequencies
|
298
|
+
reduce(Hash.new(0)) do |counts, x|
|
299
|
+
counts.update!(x, :inc)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
262
303
|
def doall(n = nil)
|
263
304
|
if n
|
264
305
|
loop(n, seq) do |recur, n, s|
|
data/lib/rupture.rb
CHANGED
data/rupture.gemspec
CHANGED
@@ -1,63 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
-
# -*- encoding: utf-8 -*-
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
3
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "rupture"
|
6
|
+
gem.version = IO.read('VERSION')
|
7
|
+
gem.authors = ["Justin Balthrop", "Alan Malloy"]
|
8
|
+
gem.email = ["git@justinbalthrop.com"]
|
9
|
+
gem.description = %q{Clojure sequence functions for Ruby.}
|
10
|
+
gem.summary = gem.description
|
11
|
+
gem.homepage = "https://github.com/flatland/rupture"
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
s.email = %q{code@justinbalthrop.com}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE",
|
17
|
-
"README.md"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
"LICENSE",
|
21
|
-
"README.md",
|
22
|
-
"Rakefile",
|
23
|
-
"VERSION",
|
24
|
-
"init.rb",
|
25
|
-
"lib/rupture.rb",
|
26
|
-
"lib/rupture/array_seq.rb",
|
27
|
-
"lib/rupture/cons.rb",
|
28
|
-
"lib/rupture/core_ext.rb",
|
29
|
-
"lib/rupture/fn.rb",
|
30
|
-
"lib/rupture/function.rb",
|
31
|
-
"lib/rupture/lazy_seq.rb",
|
32
|
-
"lib/rupture/list.rb",
|
33
|
-
"lib/rupture/lookup.rb",
|
34
|
-
"lib/rupture/meta.rb",
|
35
|
-
"lib/rupture/rails_ext.rb",
|
36
|
-
"lib/rupture/reader.rb",
|
37
|
-
"lib/rupture/seq.rb",
|
38
|
-
"lib/rupture/sequence.rb",
|
39
|
-
"lib/rupture/symbol.rb",
|
40
|
-
"lib/rupture/utils.rb",
|
41
|
-
"rupture.gemspec",
|
42
|
-
"test/fn_test.rb",
|
43
|
-
"test/list_test.rb",
|
44
|
-
"test/meta_test.rb",
|
45
|
-
"test/seq_test.rb",
|
46
|
-
"test/test_helper.rb"
|
47
|
-
]
|
48
|
-
s.homepage = %q{http://github.com/flatland/rupture}
|
49
|
-
s.require_paths = ["lib"]
|
50
|
-
s.rubygems_version = %q{1.3.7}
|
51
|
-
s.summary = %q{Clojure sequence functions for Ruby.}
|
13
|
+
gem.add_development_dependency 'shoulda', '3.0.1'
|
14
|
+
gem.add_development_dependency 'mocha'
|
15
|
+
gem.add_development_dependency 'hamster'
|
16
|
+
gem.add_development_dependency 'rake'
|
52
17
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
|
-
else
|
59
|
-
end
|
60
|
-
else
|
61
|
-
end
|
18
|
+
gem.files = `git ls-files`.split($/)
|
19
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
20
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
|
+
gem.require_paths = ["lib"]
|
62
22
|
end
|
63
|
-
|
data/test/fn_test.rb
CHANGED
@@ -13,6 +13,11 @@ class FnTest < Test::Unit::TestCase
|
|
13
13
|
# assert_equal [1,2,3,4,5,6].seq, F[:concat].apply([1],[2],[[3,4],[5,6]])
|
14
14
|
# end
|
15
15
|
|
16
|
+
should "fnil replaces nil args" do
|
17
|
+
assert_equal [2,3,4,1,5].seq, [1,2,3,nil,4].seq.map(:inc.fnil(0))
|
18
|
+
assert_equal [1,2,nil,1].seq, [:a,:b,:c,nil].seq.map({:a => 1, :b => 2}.fnil(:a))
|
19
|
+
end
|
20
|
+
|
16
21
|
should "use hash as fn" do
|
17
22
|
assert_equal [1, 2, 3, nil].seq, [:foo, :bar, :baz, :bam].seq.map({:foo => 1, :bar => 2, :baz => 3})
|
18
23
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class FunctionTest < Test::Unit::TestCase
|
4
|
+
should "loop" do
|
5
|
+
n = F.loop(0) do |recur, i|
|
6
|
+
if i < 10
|
7
|
+
recur[i.inc]
|
8
|
+
else
|
9
|
+
i
|
10
|
+
end
|
11
|
+
end
|
12
|
+
assert_equal 10, n
|
13
|
+
end
|
14
|
+
|
15
|
+
should "loop lazily" do
|
16
|
+
s = F.lazy_loop(0) do |recur, i|
|
17
|
+
if i != 10
|
18
|
+
F.cons(i, recur[i.inc])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
assert_equal (0..9).seq, s
|
22
|
+
end
|
23
|
+
|
24
|
+
should "comprehend lists" do
|
25
|
+
assert_equal [[1,3],[1,4],[1,5],[2,3],[2,4],[2,5],[3,3],[3,4],[3,5]].seq,
|
26
|
+
F.for([1,2,3], [3,4,5]) {|a, b| [a,b]}
|
27
|
+
end
|
28
|
+
|
29
|
+
should "let stuff" do
|
30
|
+
a = 0
|
31
|
+
c = F.let(1,2) do |a,b|
|
32
|
+
assert_equal 1, a
|
33
|
+
assert_equal 2, b
|
34
|
+
a + b
|
35
|
+
end
|
36
|
+
assert_equal 3, c
|
37
|
+
|
38
|
+
assert_equal a, 0 if RUBY_VERSION =~ /1.9/ # 1.8 only has lexical scope for functions.
|
39
|
+
|
40
|
+
c = 1.let(2) do |a,b|
|
41
|
+
assert_equal 1, a
|
42
|
+
assert_equal 2, b
|
43
|
+
a + b
|
44
|
+
end
|
45
|
+
assert_equal 3, c
|
46
|
+
|
47
|
+
assert_equal a, 0 if RUBY_VERSION =~ /1.9/ # 1.8 only has lexical scope for functions.
|
48
|
+
end
|
49
|
+
|
50
|
+
should "when_let" do
|
51
|
+
assert_equal nil, F.when_let(false) {|a| a + 1}
|
52
|
+
assert_equal nil, F.when_let(nil) {|a| a + 1}
|
53
|
+
assert_equal 2, F.when_let(1) {|a| a + 1}
|
54
|
+
end
|
55
|
+
|
56
|
+
should "fix stuff" do
|
57
|
+
assert_equal 1, F.fix(1,:even?,:inc)
|
58
|
+
assert_equal 1, 1.fix(:even?,:inc)
|
59
|
+
|
60
|
+
assert_equal 3, F.fix(2,:even?,:inc)
|
61
|
+
assert_equal 3, 2.fix(:even?,:inc)
|
62
|
+
|
63
|
+
assert_equal 12, F.fix(2,:even?) {|i| i + 10}
|
64
|
+
assert_equal 12, 2.fix(:even?) {|i| i + 10}
|
65
|
+
end
|
66
|
+
end
|
data/test/map_test.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class MapTest < Test::Unit::TestCase
|
4
|
+
should "destructure" do
|
5
|
+
a,b,c,d = {:foo => 1, :bar => 3}.destruct(:bar, :foo, :bar, :baz)
|
6
|
+
|
7
|
+
assert_equal 3, a
|
8
|
+
assert_equal 1, b
|
9
|
+
assert_equal 3, c
|
10
|
+
assert_equal nil, d
|
11
|
+
|
12
|
+
{:foo => 1, :bar => 3}.destruct(:bar, :foo, :bar, :baz) do |*args|
|
13
|
+
assert_equal [3, 1, 3, nil], args
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
should "update!" do
|
18
|
+
h = {:foo => 1, :bar => [1,2,3].seq}
|
19
|
+
h.update!(:foo, :inc)
|
20
|
+
|
21
|
+
assert_equal({:foo => 2, :bar => [1,2,3].seq}, h)
|
22
|
+
|
23
|
+
h.update!(:bar, :map) {|i| i + 10}
|
24
|
+
assert_equal({:foo => 2, :bar => [11,12,13].seq}, h)
|
25
|
+
|
26
|
+
h.update!(:foo) {|i| i + 10}
|
27
|
+
assert_equal({:foo => 12, :bar => [11,12,13].seq}, h)
|
28
|
+
end
|
29
|
+
|
30
|
+
should "update" do
|
31
|
+
h = ~{:foo => 1, :bar => [1,2,3].seq}
|
32
|
+
|
33
|
+
assert_equal ~{:foo => 2, :bar => [1,2,3].seq}, h.update(:foo, :inc)
|
34
|
+
assert_equal ~{:foo => 1, :bar => [11,12,13].seq}, h.update(:bar, :map) {|i| i + 10}
|
35
|
+
assert_equal ~{:foo => 11, :bar => [1,2,3].seq}, h.update(:foo) {|i| i + 10}
|
36
|
+
end
|
37
|
+
end
|
data/test/seq_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/test_helper'
|
2
2
|
|
3
3
|
class SeqTest < Test::Unit::TestCase
|
4
|
-
empty_seqs = [nil, [], Rupture::Seq
|
4
|
+
empty_seqs = [nil, [], Rupture::Seq::Empty, F.lazy_seq{nil}, F.lazy_seq{[]}]
|
5
5
|
|
6
6
|
def numbers(i)
|
7
7
|
F.lazy_seq { F.cons(i, numbers(i.inc))}
|
@@ -56,9 +56,9 @@ class SeqTest < Test::Unit::TestCase
|
|
56
56
|
end
|
57
57
|
|
58
58
|
should "reverse and rseq" do
|
59
|
-
assert_equal [1,2,3,4].seq,
|
60
|
-
assert_equal [1,2,3,4].seq,
|
61
|
-
assert_equal Rupture::Seq
|
59
|
+
assert_equal [1,2,3,4].seq, [4,3,2,1].seq.reverse
|
60
|
+
assert_equal [1,2,3,4].seq, [4,3,2,1].rseq
|
61
|
+
assert_equal Rupture::Seq::Empty, [].seq.reverse
|
62
62
|
end
|
63
63
|
|
64
64
|
should "count" do
|
@@ -80,6 +80,10 @@ class SeqTest < Test::Unit::TestCase
|
|
80
80
|
assert_equal [9,12,15].seq, [3,4,5].seq.map {|a| a * 3}
|
81
81
|
end
|
82
82
|
|
83
|
+
should "map_indexed" do
|
84
|
+
assert_equal [2,4,6,8].seq, [2,3,4,5].seq.map_indexed {|i, a| i + a}
|
85
|
+
end
|
86
|
+
|
83
87
|
should "concat" do
|
84
88
|
assert_equal [1,2,3,4,5,6].seq, F.concat([1,2],[3,4,5],[6])
|
85
89
|
assert_equal [1,2,3,4,5,6].seq, [1,2].seq.concat([3,4,5],[6])
|
@@ -99,6 +103,11 @@ class SeqTest < Test::Unit::TestCase
|
|
99
103
|
assert_equal 240, [1,2,3,4].seq.reduce(:*, 10)
|
100
104
|
end
|
101
105
|
|
106
|
+
should "reducutions" do
|
107
|
+
assert_equal [1,3,6,10].seq, [1,2,3,4].seq.reductions(:+)
|
108
|
+
assert_equal [10,10,20,60,240].seq, [1,2,3,4].seq.reductions(:*, 10)
|
109
|
+
end
|
110
|
+
|
102
111
|
should "foldr" do
|
103
112
|
assert_equal F.list(1,2,3,4), [1,2,3,4].seq.foldr(F[:cons], nil)
|
104
113
|
end
|
@@ -252,4 +261,13 @@ class SeqTest < Test::Unit::TestCase
|
|
252
261
|
should "partition_between" do
|
253
262
|
assert_equal [[1], [nil, 4, 3], [nil, 8]].seq.map(:seq), [1,nil,4,3,nil,8].seq.partition_between {|a,b| b == nil}
|
254
263
|
end
|
264
|
+
|
265
|
+
should "sort_by" do
|
266
|
+
assert_equal [[:foo,1],[:baz,2],[:bar,5]].seq,
|
267
|
+
[[:foo,1],[:bar,5],[:baz,2]].seq.sort_by(:last)
|
268
|
+
end
|
269
|
+
|
270
|
+
should "calculate frequencies" do
|
271
|
+
assert_equal({1=>3, nil=>2, 2=>1}, [1,nil,2,1,nil,1].seq.frequencies)
|
272
|
+
end
|
255
273
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,48 +1,110 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rupture
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Justin Balthrop
|
14
|
+
- Alan Malloy
|
14
15
|
autorequire:
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date:
|
19
|
+
date: 2013-08-13 00:00:00 -07:00
|
19
20
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: shoulda
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - "="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 5
|
31
|
+
segments:
|
32
|
+
- 3
|
33
|
+
- 0
|
34
|
+
- 1
|
35
|
+
version: 3.0.1
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: mocha
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 3
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
version: "0"
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: hamster
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: rake
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id004
|
22
80
|
description: Clojure sequence functions for Ruby.
|
23
|
-
email:
|
81
|
+
email:
|
82
|
+
- git@justinbalthrop.com
|
24
83
|
executables: []
|
25
84
|
|
26
85
|
extensions: []
|
27
86
|
|
28
|
-
extra_rdoc_files:
|
29
|
-
|
30
|
-
- README.md
|
87
|
+
extra_rdoc_files: []
|
88
|
+
|
31
89
|
files:
|
90
|
+
- .gitignore
|
91
|
+
- .rbenv-version
|
92
|
+
- Gemfile
|
32
93
|
- LICENSE
|
33
94
|
- README.md
|
34
95
|
- Rakefile
|
35
96
|
- VERSION
|
36
|
-
- init.rb
|
37
97
|
- lib/rupture.rb
|
38
98
|
- lib/rupture/array_seq.rb
|
39
99
|
- lib/rupture/cons.rb
|
40
100
|
- lib/rupture/core_ext.rb
|
41
101
|
- lib/rupture/fn.rb
|
42
102
|
- lib/rupture/function.rb
|
103
|
+
- lib/rupture/hash_map.rb
|
43
104
|
- lib/rupture/lazy_seq.rb
|
44
105
|
- lib/rupture/list.rb
|
45
106
|
- lib/rupture/lookup.rb
|
107
|
+
- lib/rupture/map.rb
|
46
108
|
- lib/rupture/meta.rb
|
47
109
|
- lib/rupture/rails_ext.rb
|
48
110
|
- lib/rupture/reader.rb
|
@@ -52,12 +114,14 @@ files:
|
|
52
114
|
- lib/rupture/utils.rb
|
53
115
|
- rupture.gemspec
|
54
116
|
- test/fn_test.rb
|
117
|
+
- test/function_test.rb
|
55
118
|
- test/list_test.rb
|
119
|
+
- test/map_test.rb
|
56
120
|
- test/meta_test.rb
|
57
121
|
- test/seq_test.rb
|
58
122
|
- test/test_helper.rb
|
59
123
|
has_rdoc: true
|
60
|
-
homepage:
|
124
|
+
homepage: https://github.com/flatland/rupture
|
61
125
|
licenses: []
|
62
126
|
|
63
127
|
post_install_message:
|
@@ -86,9 +150,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
150
|
requirements: []
|
87
151
|
|
88
152
|
rubyforge_project:
|
89
|
-
rubygems_version: 1.
|
153
|
+
rubygems_version: 1.5.2
|
90
154
|
signing_key:
|
91
155
|
specification_version: 3
|
92
156
|
summary: Clojure sequence functions for Ruby.
|
93
|
-
test_files:
|
94
|
-
|
157
|
+
test_files:
|
158
|
+
- test/fn_test.rb
|
159
|
+
- test/function_test.rb
|
160
|
+
- test/list_test.rb
|
161
|
+
- test/map_test.rb
|
162
|
+
- test/meta_test.rb
|
163
|
+
- test/seq_test.rb
|
164
|
+
- test/test_helper.rb
|
data/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'rupture'
|