mll 0.4.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +100 -29
  3. data/Rakefile +29 -2
  4. data/lib/mll.rb +77 -63
  5. data/mll.gemspec +1 -1
  6. data/spec/_spec.rb +347 -223
  7. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1826ae5ecd9c663432d75b8d35387762e7d3ce85
4
- data.tar.gz: 1387511b30a66d4331ab6fe978682dfc0e4607af
3
+ metadata.gz: 812325964160837ba11226c5585e372463dd9fd7
4
+ data.tar.gz: fb060b7f5a405a230b21a20c9cc8426e5bd7e19b
5
5
  SHA512:
6
- metadata.gz: 3e791cfab9e6f9e979b3c68a6364a2e8fd362696ae7592083f934165a543a525828f9b7504d137a32984988490c22b9e2bc891df7542f94bbcc1c3241b9c3e94
7
- data.tar.gz: 71d9d47ec39472b8666db934b34fb33042a18b64e5543f0e8c19c1a39982e035335bf1075521069732014529b49ae59f298cb88f6ed10daea28c3f1dd13b255a
6
+ metadata.gz: 5fe15ad50531a0b9740847b01c27ed76e598d0345a0158b22474715a7fbb8bc917479c406ef1cac3580b167dd19b30bca820665888792657e8be6bc2a1f67b81
7
+ data.tar.gz: dd3547ff6b62c5ecd29d91529aee42e1552cb0d90bde926e7d8e090cdcf1df7ca1792aa110835a82133127f2f33d2c56a6421ab2ca56e778acbead64537c7380
data/README.md CHANGED
@@ -2,39 +2,62 @@
2
2
 
3
3
  ## What
4
4
 
5
- Этот гем не ставит перед собой цель полностью имитировать типы данных, синтаксис Wolfram Mathematica Language, или научить Ruby крутым визуализациям. Целью является вложить в Ruby мощь стандартной библиотеки. В перспективе визуализация возможна при помощи других гемов.
5
+ This gem isn't supposed to mimic all data types, exact syntax of Wolfram Mathematica or make Ruby able to make the same visualisations.
6
+
7
+ The main goal is to make Ruby more powerful by including the most used functions, that Ruby lacks, such as `Table[]`, `FoldList[]`, etc. Visualisations are possible later by using additional gems.
6
8
 
7
9
  ## Why
8
10
 
9
- 1. Важной является реализация https://reference.wolfram.com/language/ref/Listable.html: автоматическое применение функции ко всем элементам аргумента, если тот является List-ом (Array-ем в среде Ruby).
10
- 2. `::range`, в отличие от рубишного, может иметь отрицательный step.
11
- 3. `::table`, в отличие от рубишного `.map`, может создавать многомерные массивы одним вызовом, а не только вложенными.
11
+ 1. One of the most useful things is automatic zipping vectors (`Array`s) when you apply scalar functions to them. https://reference.wolfram.com/language/ref/Listable.html
12
+ 2. unlike Ruby's `Range` class, `::range` can handle negative `step` and even have `float` starting value
13
+ 3. unlike Ruby's `Array#map`, `::table` can create multidimensional arrays with a single call, not nested
14
+ 4. `#fold_list` was wanted [here](http://stackoverflow.com/q/1475808/322020) in Ruby while being already implemented as [FoldList[]](http://reference.wolfram.com/language/ref/FoldList.html) in Mathematica and [scanl](http://hackage.haskell.org/package/base-4.8.0.0/docs/Prelude.html#v:scanl) in Haskell
15
+ 5. `#nest` (n times) and `#nest_list` for repetitive applying the same function -- `#nest_while` and `#nest_while_list` are going to be implemented later
12
16
 
13
17
  ## How
14
18
 
15
- MLL::range(2, 3) #=> 2..3
16
- MLL::range([2, 3]) #=> [1..2, 1..3]
17
- MLL::range(1..3) #=> [1..1, 1..2, 1..3]
18
-
19
- MLL::table ->(i,j){ i+j }, [[1, 0, 1]], [[0, 2, 0]]
20
- #=> [[1, 3, 1],
21
- [0, 2, 0],
22
- [1, 3, 1]]
23
- MLL::table ->(i,j,k){ 100*i + 10*j + k }, 2, 3, 4
24
- # => [[[111, 112, 113, 114], [121, 122, 123, 124], [131, 132, 133, 134]],
25
- [[211, 212, 213, 214], [221, 222, 223, 224], [231, 232, 233, 234]]]
26
-
27
- MLL::table MLL.method(:times), 9, 9
28
- # => [[1, 2, 3, 4, 5, 6, 7, 8, 9],
29
- [2, 4, 6, 8, 10, 12, 14, 16, 18],
30
- [3, 6, 9, 12, 15, 18, 21, 24, 27],
31
- [4, 8, 12, 16, 20, 24, 28, 32, 36],
32
- [5, 10, 15, 20, 25, 30, 35, 40, 45],
33
- [6, 12, 18, 24, 30, 36, 42, 48, 54],
34
- [7, 14, 21, 28, 35, 42, 49, 56, 63],
35
- [8, 16, 24, 32, 40, 48, 56, 64, 72],
36
- [9, 18, 27, 36, 45, 54, 63, 72, 81]]
37
-
19
+ MLL::range[ 2, 3 ] # => 2..3
20
+ MLL::range[[2, 3]] # => [1..2, 1..3]
21
+ MLL::range[ 1..3 ] # => [1..1, 1..2, 1..3]
22
+
23
+ MLL::table[ ->(i,j){ i+j }, [[1, 0, 1]], [[0, 2, 0]] ]
24
+ # => [[1, 3, 1],
25
+ [0, 2, 0],
26
+ [1, 3, 1]]
27
+ MLL::table[ MLL.method(:times), 9, 9 ]
28
+ # => [[1, 2, 3, 4, 5, 6, 7, 8, 9],
29
+ [2, 4, 6, 8, 10, 12, 14, 16, 18],
30
+ [3, 6, 9, 12, 15, 18, 21, 24, 27],
31
+ [4, 8, 12, 16, 20, 24, 28, 32, 36],
32
+ [5, 10, 15, 20, 25, 30, 35, 40, 45],
33
+ [6, 12, 18, 24, 30, 36, 42, 48, 54],
34
+ [7, 14, 21, 28, 35, 42, 49, 56, 63],
35
+ [8, 16, 24, 32, 40, 48, 56, 64, 72],
36
+ [9, 18, 27, 36, 45, 54, 63, 72, 81]]
37
+
38
+ MLL::fold_list[ MLL::times, MLL::range[10] ]
39
+ # => [1,2,6,24,120,720,5040,40320,362880,3628800]
40
+
41
+ # here is `Listable' magic, allowing to zip arrays
42
+ # even of different dimensions with basic operations
43
+ MLL::times[ [[1,2],[3,4]], [5,6] ]
44
+ # => [[5,10], [18,24]]
45
+ # ::times means *
46
+ # ::divide means /
47
+ # ::subtract means -
48
+ # ::plus means +
49
+
50
+ # http://en.wikipedia.org/wiki/Collatz_conjecture
51
+ MLL::nest_list[ ->(i){ i.even? ? i/2 : (i*3+1)/2 }, 20, 10 ]
52
+ # => [20, 10, 5, 8, 4, 2, 1, 2, 1, 2, 1]
53
+
54
+ MLL::fold_list[ ->(a,b){ 10*a + b }, 0, [4,5,1,6,7,8] ]
55
+ # => [0,4,45,451,4516,45167,451678]
56
+
57
+ MLL::subdivide[ 5, 10, 4 ]
58
+ # => [5.0, 6.25, 7.5, 8.75, 10.0]
59
+
60
+ Note that to see some of above examples working you need `.to_a`, `.map(&:to_a)` or even `.to_a.map(&:to_a)` since lazyness is intensively used.
38
61
 
39
62
  ## Installation
40
63
 
@@ -46,5 +69,53 @@
46
69
 
47
70
  or
48
71
 
49
- rake
50
-
72
+ rake spec
73
+
74
+ ## TODO (this section is filled automatically by `rake todo` task -- do not remove)
75
+
76
+ #### lib/mll.rb
77
+
78
+ ```
79
+ module MLL
80
+ def self.fold_list
81
+ lambda do |f, x, list = nil|
82
+ # TODO use Ruby#inject ?
83
+ def self.table
84
+ lambda do |f, *args|
85
+ [].tap do |result|
86
+ }]].tap do |stack|
87
+ stack.each do |ai, ri|
88
+ # TODO try to make #table lazy (Enumerator instead of Array)
89
+ # TODO not sure if we need any other kind of Listability except of #range[[Array]]
90
+ ```
91
+
92
+ #### spec/_spec.rb
93
+
94
+ ```
95
+ # TODO move Properties & Relations to some separate contexts maybe
96
+ # TODO @fraggedICE wishes using Rational -- would also allow implementing more examples
97
+ # TODO elegantly get rid of repetitive type checks
98
+ # TODO ? let(:fake_lambda){ ->(*args){fail} }
99
+ describe MLL do
100
+ describe "List Manipulation" do
101
+ describe "Constructing Lists" do
102
+ describe "#table" do
103
+ describe "Scope" do
104
+ # TODO: "Make a triangular array:"
105
+ describe "#range" do
106
+ # TODO take from docs more examples that involve other functions
107
+ describe "Applying Functions to Lists" do
108
+ describe "#fold_list" do
109
+ describe "Applications" do
110
+ # TODO maybe move it to README.md
111
+ # TODO http://reference.wolfram.com/language/guide/ElementsOfLists.html
112
+ # TODO http://reference.wolfram.com/language/guide/RearrangingAndRestructuringLists.html
113
+ # TODO http://reference.wolfram.com/language/guide/MathematicalAndCountingOperationsOnLists.html
114
+ describe "Functional Programming" do
115
+ describe "Iteratively Applying Functions" do
116
+ # TODO move #nest_list and #fold_list and others here?
117
+ describe "#nest" do
118
+ # TODO neat graphic examples
119
+ # TODO http://reference.wolfram.com/language/guide/HandlingArraysOfData.html
120
+ # TODO http://reference.wolfram.com/language/guide/ComputationWithStructuredDatasets.html
121
+ ```
data/Rakefile CHANGED
@@ -1,9 +1,36 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
+ desc "Append TODO-list to README.md"
5
+ task :todo do |t|
6
+ fail unless File.exist? "README.md"
7
+ File.open("README.md", "r+") do |readme_file|
8
+ # begin readme_file.gets end until $_[/^## TODO/]
9
+ readme_file.gets "## TODO"
10
+ readme_file.gets
11
+ %w{ lib/mll.rb spec/_spec.rb }.each do |file|
12
+ readme_file.puts "", "#### #{file}", "", "```"
13
+ stack = []
14
+ lines = []
15
+ File.foreach(file).with_index do |line, i|
16
+ next unless shift = /\S/ =~ line
17
+ depth = shift / 2
18
+ stack[depth] = i
19
+ lines |= stack.take(depth + 1) if line[/^\s*# TODO/]
20
+ end
21
+ next_line_to_print = lines.shift
22
+ File.foreach(file).with_index do |line, i|
23
+ next unless i == next_line_to_print
24
+ readme_file.puts line
25
+ next_line_to_print = lines.shift
26
+ end
27
+ readme_file.puts "```"
28
+ end
29
+ end
30
+ end
31
+
4
32
  RSpec::Core::RakeTask.new(:spec) do |t|
5
33
  t.verbose = false
6
34
  end
7
35
 
8
- task :default => :spec
9
-
36
+ task :default => %w{ todo spec }
data/lib/mll.rb CHANGED
@@ -1,74 +1,83 @@
1
1
  module MLL
2
2
 
3
- def self.nest_list f, expr, n
4
- Enumerator.new do |e|
5
- e << expr
6
- n.times do
7
- e << expr = f.call(expr)
3
+ def self.nest_list
4
+ lambda do |f, expr, n|
5
+ Enumerator.new do |e|
6
+ e << expr
7
+ n.times do
8
+ e << expr = f.call(expr)
9
+ end
8
10
  end
9
11
  end
10
12
  end
11
13
  # def self.nest *args
12
14
  # nest_list(*args).last
13
15
  # end
14
- def self.nest f, expr, n
15
- n.times{ expr = f.call expr }
16
- expr
16
+ def self.nest
17
+ lambda do |f, expr, n|
18
+ n.times{ expr = f.call expr }
19
+ expr
20
+ end
17
21
  end
18
22
 
19
- def self.fold_list f, x, list = nil
20
- # TODO teach it to accept Range ?
21
- x, *list = x unless list
22
- # use Ruby#inject ?
23
- Enumerator.new do |e|
24
- e << x
25
- list.each do |i|
26
- e << x = f.call(x, i)
23
+ def self.fold_list
24
+ lambda do |f, x, list = nil|
25
+ x, *list = x.to_a unless list
26
+ # TODO use Ruby#inject ?
27
+ Enumerator.new do |e|
28
+ e << x
29
+ list.each do |i|
30
+ e << x = f.call(x, i)
31
+ end
27
32
  end
28
33
  end
29
34
  end
30
35
 
31
- def self.table f, *args
32
- [].tap do |result|
33
- [[result, args.map{ |r| # add lazy?
34
- r.respond_to?(:map) && r.first.respond_to?(:map) ?
35
- r.first : range(*r)
36
- }]].tap do |stack|
37
- stack.each do |ai, ri|
38
- # TODO try to make #table lazy (Enumerator instead of Array)
39
- # "no implicit conversion of Enumerator::Lazy into Array"
40
- # "undefined method `replace' for #<Enumerator::Lazy: []>"
41
- ai.replace ri.first.map{ |i|
42
- if ri.size == 1
43
- f.call(*ai, i)
44
- else
45
- [*ai.dup, i].tap{ |t| stack << [t, ri.drop(1)] }
46
- end
47
- }#.to_a # WTF
36
+ def self.table
37
+ lambda do |f, *args|
38
+ [].tap do |result|
39
+ [[result, args.map{ |r| # add lazy?
40
+ r.respond_to?(:map) && r.first.respond_to?(:map) ?
41
+ r.first : range[*r]
42
+ }]].tap do |stack|
43
+ stack.each do |ai, ri|
44
+ # TODO try to make #table lazy (Enumerator instead of Array)
45
+ # "no implicit conversion of Enumerator::Lazy into Array"
46
+ # "undefined method `replace' for #<Enumerator::Lazy: []>"
47
+ ai.replace ri.first.map{ |i|
48
+ if ri.size == 1
49
+ f.call(*ai, i)
50
+ else
51
+ [*ai.dup, i].tap{ |t| stack << [t, ri.drop(1)] }
52
+ end
53
+ }
54
+ end
48
55
  end
49
56
  end
50
57
  end
51
58
  end
52
59
 
53
- # http://reference.wolfram.com/language/ref/Listable.html
54
60
  def self.define_listable_function name, &block
55
- (class << self; self end).class_eval do # http://stackoverflow.com/a/12792313/322020
56
- define_method name do |*args|
57
- case args.map{ |i| i.respond_to? :map }
58
- when [true] ; args.first.lazy.map &method(name)
59
- when [true, true] ; args.first.lazy.zip(args.last).map{ |i, j| send name, i, j }
60
- when [true, false] ; args.first.lazy.map{ |i| send name, i, args.last }
61
- when [false, true] ; args.last.lazy.map{ |i| send name, args.first, i }
62
- else
63
- block.call *args
61
+ (class << self; self end).class_eval do
62
+ define_method name do
63
+ lambda do |*args|
64
+ case args.map{ |i| i.respond_to? :map }
65
+ when [true] ; args.first.lazy.map &method(name).call
66
+ when [true, true] ; args.first.lazy.zip(args.last).map{ |i, j| send(name)[i, j] }
67
+ when [true, false] ; args.first.lazy.map{ |i| send(name)[i, args.last] }
68
+ when [false, true] ; args.last.lazy.map{ |i| send(name)[args.first, i] }
69
+ else
70
+ block.call *args
71
+ end
64
72
  end
65
73
  end
66
74
  end
67
75
  end
68
76
 
77
+ # TODO not sure if we need any other kind of Listability except of #range[[Array]]
69
78
  define_listable_function :range do |*args|
70
79
  case args.size
71
- when 1 ; range 1, args[0] # TODO do smth with #table(-n)
80
+ when 1 ; range[1, args[0]] # TODO do smth with #table(-n)
72
81
  when 2 ; Range.new(args[0], args[1]).step
73
82
  when 3
74
83
  case args[2] <=> 0
@@ -89,34 +98,39 @@ module MLL
89
98
  end
90
99
  end
91
100
 
92
- define_listable_function (:subtract) { |a, b| a - b }
93
- define_listable_function (:divide) { |a, b| a / b }
94
- define_listable_function (:_plus) { |a, b| a + b }
95
- define_listable_function (:_times) { |a, b| a * b }
101
+ define_listable_function(:subtract) { |*args| raise ArgumentError.new("need two arguments") unless args.size == 2 ; args[0] - args[1] }
102
+ define_listable_function(:divide) { |*args| raise ArgumentError.new("need two arguments") unless args.size == 2 ; args[0] / args[1] }
103
+ define_listable_function(:_plus) { |*args| raise ArgumentError.new("need two arguments") unless args.size == 2 ; args[0] + args[1] }
104
+ define_listable_function(:_times) { |*args| raise ArgumentError.new("need two arguments") unless args.size == 2 ; args[0] * args[1] }
105
+ # define_listable_function (:power) { |*args| raise ArgumentError.new("need two arguments") unless args.size == 2 ; args[0] ** args[1] }
96
106
 
97
107
  # http://reference.wolfram.com/language/ref/Orderless.html
98
- def self.define_orderless_function name, &block
108
+ def self.define_orderless_function name, start, &block
99
109
  (class << self; self end).class_eval do # http://stackoverflow.com/a/12792313/322020
100
- define_method name do |*args|
101
- args.inject do |memo, obj|
102
- block.call memo, obj
110
+ define_method name do
111
+ lambda do |*args|
112
+ args.inject(start) do |memo, obj|
113
+ block.call memo, obj
114
+ end
103
115
  end
104
116
  end
105
117
  end
106
118
  end
107
119
 
108
- define_orderless_function (:plus) { |a, b| _plus a, b }
109
- define_orderless_function (:times) { |a, b| _times a, b }
120
+ define_orderless_function(:plus, 0) { |a, b| _plus.call a, b }
121
+ define_orderless_function(:times, 1) { |a, b| _times.call a, b }
110
122
 
111
- def self.subdivide *args
112
- case args.size
113
- when 1 ; subdivide 1, args[0]
114
- when 2 ; subdivide 0, args[0], args[1]
115
- when 3 ; range(args[0], args[1], (args[1] - args[0]) * 1.0 / args[2])
116
- ## using only pure Ruby methods makes unittests more reliable
117
- # when 3 ; plus args[0], divide(times(1.0, args[1] - args[0], range(0, args[2])), args[2])
118
- else
119
- raise ArgumentError.new("wrong number of arguments (#{args.size} for 1..3)")
123
+ def self.subdivide
124
+ lambda do |*args|
125
+ case args.size
126
+ when 1 ; subdivide[1, args[0]]
127
+ when 2 ; subdivide[0, args[0], args[1]]
128
+ when 3
129
+ # raise ArgumentError.new("can't divide into 0 parts") if args[2].zero?
130
+ range[args[0], args[1], (args[1] - args[0]) * 1.0 / args[2]]
131
+ else
132
+ raise ArgumentError.new("wrong number of arguments (#{args.size} for 1..3)")
133
+ end
120
134
  end
121
135
  end
122
136
 
data/mll.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "mll"
3
- spec.version = "0.4.0"
3
+ spec.version = "1.1.0"
4
4
  spec.authors = ["Victor Maslov"]
5
5
  spec.email = ["nakilon@gmail.com"]
6
6
  spec.summary = "Mathematica Language Library in Ruby"
data/spec/_spec.rb CHANGED
@@ -5,26 +5,23 @@ require_relative File.join "..", "lib", "mll"
5
5
  # PERMATODO test all types returned (not actually all but about lazyness)
6
6
  # PERMATODO check for "Details" paragraphs to make more trivial examples
7
7
  # PERMATODO leave here only useful tests, not demostrations -- move them to README.rb
8
- # PERMATODO single element Arrays tests
8
+ # PERMATODO polymorphic functions should have ArgumentError-s and tests about that
9
9
  # PERMATODO move Properties & Relations to some separate contexts maybe
10
10
 
11
- # TODO test all implemented exceptions
12
- # TODO test all types returned (not actually all but about lazyness)
13
- # TODO check for "Details" paragraphs to make more trivial examples
14
- # TODO leave here only useful tests, not demostrations -- move them to README.rb
15
- # TODO single element Arrays tests
16
11
  # TODO move Properties & Relations to some separate contexts maybe
17
12
 
18
13
  # TODO @fraggedICE wishes using Rational -- would also allow implementing more examples
19
- # TODO rake task for appending TODOs and pendings to README.rb
20
- # TODO rename examples according to their description not content
21
14
  # TODO elegantly get rid of repetitive type checks
22
- # TODO implement #power for use in unittests
23
- # TODO let(:fake_lambda){ ->(*args){fail} } ?
15
+ # TODO ? let(:fake_lambda){ ->(*args){fail} }
16
+
24
17
 
25
18
  # http://reference.wolfram.com/language/guide/LanguageOverview.html
26
19
  describe MLL do
27
20
 
21
+ def method_missing *args
22
+ described_class.send *args
23
+ end
24
+
28
25
  # http://reference.wolfram.com/language/guide/Syntax.html
29
26
  describe "Syntax" do
30
27
 
@@ -34,19 +31,38 @@ describe MLL do
34
31
  # http://reference.wolfram.com/language/ref/Subtract.html
35
32
  describe "#subtract" do
36
33
 
34
+ example "less than 2 args raise ArgumentError" do
35
+ expect{ subtract[0 ] }.to raise_error ArgumentError
36
+ end
37
+ example "more than 2 args raise ArgumentError" do
38
+ expect{ subtract[0,0,0] }.to raise_error ArgumentError
39
+ end
40
+
37
41
  describe "Basic Examples" do
38
42
 
39
- example "subtract(n1, n2)" do
40
- expect(MLL::subtract(10,3)).to eq 7
43
+ example "subtract numbers" do
44
+ expect(subtract[10,3]).to eq 7
41
45
  end
42
46
 
43
47
  end
44
48
 
45
49
  describe "Scope" do
46
50
 
47
- example "subtract(list, n)" do
48
- expect(MLL::subtract([1,2,3,4],0.5)).to be_a Enumerator
49
- expect(MLL::subtract([1,2,3,4],0.5).to_a).to eq [0.5,1.5,2.5,3.5]
51
+ example "threads element‐wise over lists" do
52
+ expect(subtract[[1,2,3,4],0.5]).to be_a Enumerator
53
+ expect(subtract[[1,2,3,4],0.5].to_a).to eq [0.5,1.5,2.5,3.5]
54
+ expect(subtract[6,[3,2]]).to be_a Enumerator
55
+ expect(subtract[6,[3,2]].to_a).to eq [3,4]
56
+ expect(subtract[[[1,2],[3,4]],1]).to be_a Enumerator
57
+ expect(subtract[[[1,2],[3,4]],1].to_a.map(&:to_a)).to eq [[0,1],[2,3]]
58
+ expect(subtract[[3,4],[2,1]]).to be_a Enumerator
59
+ expect(subtract[[3,4],[2,1]].to_a).to eq [1,3]
60
+ expect(subtract[[[1,3],[2,4]],[1,2]]).to be_a Enumerator
61
+ expect(subtract[[[1,3],[2,4]],[1,2]].to_a.map(&:to_a)).to eq [[0,2],[0,2]]
62
+ end
63
+ example "matrices subtract element-wise" do
64
+ expect(subtract[[[5,6],[7,8]],[[4,3],[2,1]]]).to be_a Enumerator
65
+ expect(subtract[[[5,6],[7,8]],[[4,3],[2,1]]].to_a.map(&:to_a)).to eq [[1,3],[5,7]]
50
66
  end
51
67
 
52
68
  end
@@ -56,110 +72,151 @@ describe MLL do
56
72
  # http://reference.wolfram.com/language/ref/Divide.html
57
73
  describe "#divide" do
58
74
 
75
+ example "less than 2 args raise ArgumentError" do
76
+ expect{ divide[0 ] }.to raise_error ArgumentError
77
+ end
78
+ example "more than 2 args raise ArgumentError" do
79
+ expect{ divide[0,0,0] }.to raise_error ArgumentError
80
+ end
81
+
59
82
  describe "Basic Examples" do
60
83
 
61
- example "divide(n1, n2)" do
62
- # expect(MLL::divide(77,11)).to be_a Fixnum
63
- expect(MLL::divide(77,11)).to eq 7
84
+ example "divide numbers" do
85
+ expect(divide[77,11]).to eq 7
64
86
  end
65
87
 
66
88
  end
67
89
 
68
90
  describe "Scope" do
69
91
 
70
- example "divide(list, n)" do
71
- expect(MLL::divide([2,3,4,5],2.0)).to be_a Enumerator
72
- expect(MLL::divide([2,3,4,5],2.0).to_a).to eq [1,1.5,2,2.5]
92
+ example "threads element‐wise over lists" do
93
+ expect(divide[[2,3,4,5],2.0]).to be_a Enumerator
94
+ expect(divide[[2,3,4,5],2.0].to_a).to eq [1,1.5,2,2.5]
95
+ expect(divide[6,[3,2]]).to be_a Enumerator
96
+ expect(divide[6,[3,2]].to_a).to eq [2,3]
97
+ expect(divide[[[4,2],[6,8]],2]).to be_a Enumerator
98
+ expect(divide[[[4,2],[6,8]],2].to_a.map(&:to_a)).to eq [[2,1],[3,4]]
99
+ expect(divide[[2,3,4],[2,1,4]]).to be_a Enumerator
100
+ expect(divide[[2,3,4],[2,1,4]].to_a).to eq [1,3,1]
101
+ expect(divide[[[1,3],[2,4]],[1,2]]).to be_a Enumerator
102
+ expect(divide[[[1,3],[2,4]],[1,2]].to_a.map(&:to_a)).to eq [[1,3],[1,2]]
73
103
  end
74
- example "divide(list, list)" do
75
- expect(MLL::divide([2,3,4],[2,1,4])).to be_a Enumerator
76
- expect(MLL::divide([2,3,4],[2,1,4]).to_a).to eq [1,3,1]
104
+ example "matrices divide element-wise" do
105
+ expect(divide[[[5,8],[9,8]],[[1,2],[3,4]]]).to be_a Enumerator
106
+ expect(divide[[[5,8],[9,8]],[[1,2],[3,4]]].to_a.map(&:to_a)).to eq [[5,4],[3,2]]
77
107
  end
78
108
 
79
109
  end
80
110
 
81
111
  describe "Applications" do
82
112
 
83
- # TODO: "Successive ratios in a list:"
84
-
85
- end
86
-
87
- describe "Neat Examples" do
88
-
89
- example "nest_list(lambda_with_divide), n" do
90
- pending "#nestlist is yet to be implemented"
91
- fail
92
- end
93
- example "table(function, n1, n2)" do
94
- expect(MLL::table(MLL.method(:divide), [[3,6]], 4.0)).to be_a Array
95
- expect(MLL::table(MLL.method(:divide), [[6]], 4.0)).to eq [[6.0,3.0,2.0,1.5]]
113
+ example "successive ratios in a list" do
114
+ skip "waiting for Rationals to be used"
96
115
  end
97
116
 
98
117
  end
99
118
 
100
119
  end
101
120
 
102
- # TODO common tests for threading functions like times, plus, etc.
103
-
104
121
  # http://reference.wolfram.com/language/ref/Plus.html
105
122
  describe "#plus" do
106
123
 
107
- describe "Basic Examples" do
124
+ describe "Details" do
108
125
 
109
- example "plus(n1, n2, n3)" do
110
- expect(MLL::plus(2,3,4)).to eq 9
126
+ example "#plus[] is taken to be 0" do
127
+ expect(plus[]).to eq 0
111
128
  end
112
- example "plus([n1, n2, n3], n)" do
113
- expect(MLL::plus([3,4,5],2)).to be_a Enumerator
114
- expect(MLL::plus([3,4,5],2).to_a).to eq [5,6,7]
129
+ example "#plus[x] is x" do
130
+ expect(plus[5]).to eq 5
115
131
  end
116
- example "plus(n1, n2, [n3, n4])" do
117
- expect(MLL::plus(2,3,[4,5])).to be_a Enumerator
118
- expect(MLL::plus(2,3,[4,5]).to_a).to eq [9,10]
119
- end
120
- example "plus(list, list)" do
121
- expect(MLL::plus([10,20,30],[1,2,3])).to be_a Enumerator
122
- expect(MLL::plus([10,20,30],[1,2,3]).to_a).to eq [11,22,33]
132
+
133
+ end
134
+
135
+ describe "Basic Examples" do
136
+
137
+ example "sums numbers" do
138
+ expect(plus[2,3,4]).to eq 9
123
139
  end
124
- example "plus(list_of_lists, n)" do
125
- expect(MLL::plus([[1,2],[3,4]],5)).to be_a Enumerator
126
- expect(MLL::plus([[1,2],[3,4]],5).to_a.map(&:to_a)).to eq [[6,7],[8,9]]
140
+ example "threads element‐wise over lists" do
141
+ # idk why not in Scope
142
+ expect(plus[[3,4,5],2]).to be_a Enumerator
143
+ expect(plus[[3,4,5],2].to_a).to eq [5,6,7]
144
+ expect(plus[2,3,[4,5]]).to be_a Enumerator
145
+ expect(plus[2,3,[4,5]].to_a).to eq [9,10]
146
+ expect(plus[[[1,2],[3,4]],[5,6]]).to be_a Enumerator
147
+ expect(plus[[[1,2],[3,4]],[5,6]].to_a.map(&:to_a)).to eq [[6,7],[9,10]]
127
148
  end
128
- example "plus(list_of_lists, list_of_lists)" do
129
- expect(MLL::plus([[1,2],[3,4]],[[5,6],[7,8]])).to be_a Enumerator
130
- expect(MLL::plus([[1,2],[3,4]],[[5,6],[7,8]]).to_a.map(&:to_a)).to eq [[6,8],[10,12]]
149
+
150
+ end
151
+
152
+ describe "Scope" do
153
+
154
+ example "threads element‐wise over lists" do
155
+ expect(plus[[10,20,30],[1,2,3]]).to be_a Enumerator
156
+ expect(plus[[10,20,30],[1,2,3]].to_a).to eq [11,22,33]
157
+ expect(plus[[[1,2],[3,4]],5]).to be_a Enumerator
158
+ expect(plus[[[1,2],[3,4]],5].to_a.map(&:to_a)).to eq [[6,7],[8,9]]
131
159
  end
132
- example "plus([[n1, n2], [n3, n4]], [n5, n6])" do
133
- expect(MLL::plus([[1,2],[3,4]],[5,6])).to be_a Enumerator
134
- expect(MLL::plus([[1,2],[3,4]],[5,6]).to_a.map(&:to_a)).to eq [[6,7],[9,10]]
160
+ example "matrices add element-wise" do
161
+ expect(plus[[[1,2],[3,4]],[[5,6],[7,8]]]).to be_a Enumerator
162
+ expect(plus[[[1,2],[3,4]],[[5,6],[7,8]]].to_a.map(&:to_a)).to eq [[6,8],[10,12]]
135
163
  end
136
164
 
137
165
  end
138
166
 
139
- # TODO "Accumulate makes a cumulative sum:"
167
+ describe "Possible Issues" do
168
+
169
+ # idk why not in 'Properties & Relations'
170
+ example "accumulate makes a cumulative sum" do
171
+ expect(fold_list[plus, 0, [1,2,3]]).to be_a Enumerator
172
+ expect(fold_list[plus, 0, [1,2,3]].to_a).to eq [0,1,3,6]
173
+ end
174
+
175
+ end
140
176
 
141
177
  end
142
178
 
143
179
  # http://reference.wolfram.com/language/ref/Times.html
144
180
  describe "#times" do
145
181
 
182
+ describe "Details" do
183
+
184
+ example "#times[] is taken to be 1" do
185
+ expect(times[]).to eq 1
186
+ end
187
+ example "#times[x] is x" do
188
+ expect(times[5]).to eq 5
189
+ end
190
+
191
+ end
192
+
146
193
  describe "Basic Examples" do
147
194
 
148
- example "times(n1, n2, n3)" do
149
- # expect(MLL::times(2,3,4)).to be_a Fixnum
150
- expect(MLL::times(2,3,4)).to eq 24
195
+ example "multiplies numbers" do
196
+ expect(times[2,3,4]).to eq 24
151
197
  end
152
- example "times(n, [n1, n2, n3])" do
153
- expect(MLL::times(2,[3,4,5])).to be_a Enumerator
154
- expect(MLL::times(2,[3,4,5]).to_a).to eq [6,8,10]
198
+ example "threads element‐wise over lists" do
199
+ expect(times[[3,4,5],2]).to be_a Enumerator
200
+ expect(times[[3,4,5],2].to_a).to eq [6,8,10]
201
+ expect(times[2,3,[4,5]]).to be_a Enumerator
202
+ expect(times[2,3,[4,5]].to_a).to eq [24,30]
203
+ expect(times[[[1,2],[3,4]],[5,6]]).to be_a Enumerator
204
+ expect(times[[[1,2],[3,4]],[5,6]].to_a.map(&:to_a)).to eq [[5,10],[18,24]]
155
205
  end
156
- example "times(n1, n2, [n3, n4])" do
157
- expect(MLL::times(2,3,[4,5])).to be_a Enumerator
158
- expect(MLL::times(2,3,[4,5]).to_a).to eq [24,30]
206
+
207
+ end
208
+
209
+ describe "Scope" do
210
+
211
+ example "threads element‐wise over lists" do
212
+ expect(times[[2,3],[4,5]]).to be_a Enumerator
213
+ expect(times[[2,3],[4,5]].to_a).to eq [8,15]
214
+ expect(times[[[1,2],[3,4]],5]).to be_a Enumerator
215
+ expect(times[[[1,2],[3,4]],5].to_a.map(&:to_a)).to eq [[5,10],[15,20]]
159
216
  end
160
- example "times([[n1, n2], [n3, n4]], [n5, n6])" do
161
- expect(MLL::times([[1,2],[3,4]],[5,6])).to be_a Enumerator
162
- expect(MLL::times([[1,2],[3,4]],[5,6]).to_a.map(&:to_a)).to eq [[5,10],[18,24]]
217
+ example "matrices multiply element-wise" do
218
+ expect(times[[[4,3],[2,1]],[[5,6],[7,8]]]).to be_a Enumerator
219
+ expect(times[[[4,3],[2,1]],[[5,6],[7,8]]].to_a.map(&:to_a)).to eq [[20,18],[14,8]]
163
220
  end
164
221
 
165
222
  end
@@ -179,48 +236,50 @@ describe MLL do
179
236
  # http://reference.wolfram.com/language/ref/Table.html
180
237
  describe "#table" do
181
238
 
182
- describe "Basic Examples" do
239
+ describe "Details" do
183
240
 
184
- example "table(lambda, n)" do
185
- expect(MLL::table(->(i){ i**2 }, 10)).to be_a Array
186
- expect(MLL::table(->(i){ i**2 }, 10)).to eq [1,4,9,16,25,36,49,64,81,100]
241
+ example "#table[expr,spec1,spec2] is effectively equivalent to #table[#table[expr,spec2],spec1]" do
242
+ expect(table[->(i,j){ [i,j] }, 2, 3]).to eq table[->(i){ table[->(j){ [i,j] }, 3] }, 2]
187
243
  end
188
- example "table(lambda, [min, max, step])" do
189
- expect(MLL::table(->(i){ i+2 }, [0, 20, 2])).to be_a Array
190
- expect(MLL::table(->(i){ i+2 }, [0, 20, 2])).to eq [2,4,6,8,10,12,14,16,18,20,22]
244
+
245
+ end
246
+
247
+ describe "Basic Examples" do
248
+
249
+ example "a table of the first 10 squares" do
250
+ expect(table[->(i){ i**2 }, 10]).to be_a Array
251
+ expect(table[->(i){ i**2 }, 10]).to eq [1,4,9,16,25,36,49,64,81,100]
191
252
  end
192
- example "table(lambda, n1, n2)" do
193
- # TODO example to README.rb about multiplication table
194
- expect(MLL::table(->(i,j){ 10*i + j }, 4, 3)).to be_a Array
195
- expect(MLL::table(->(i,j){ 10*i + j }, 4, 3)).to eq [[11,12,13],[21,22,23],[31,32,33],[41,42,43]]
253
+ example "a table with running from 0 to 20 in steps of 2" do
254
+ expect(table[->(i){ i+2 }, [0, 20, 2]]).to be_a Array
255
+ expect(table[->(i){ i+2 }, [0, 20, 2]]).to eq [2,4,6,8,10,12,14,16,18,20,22]
196
256
  end
197
- example "table(lambda, n1, min..max, [max, min, -step])" do
198
- expect(MLL::table(->(i,j,k){ [i,j,k] }, 3, 2..3, [5, 1, -2])).to be_a Array
199
- expect(MLL::table(->(i,j,k){ [i,j,k] }, 3, 2..3, [5, 1, -2])).to eq \
200
- [
201
- [[[1, 2, 5], [1, 2, 3], [1, 2, 1]], [[1, 3, 5], [1, 3, 3], [1, 3, 1]]],
202
- [[[2, 2, 5], [2, 2, 3], [2, 2, 1]], [[2, 3, 5], [2, 3, 3], [2, 3, 1]]],
203
- [[[3, 2, 5], [3, 2, 3], [3, 2, 1]], [[3, 3, 5], [3, 3, 3], [3, 3, 1]]]
204
- ]
257
+ example "make a 4×3 matrix" do
258
+ expect(table[->(i,j){ 10*i + j }, 4, 3]).to be_a Array
259
+ expect(table[->(i,j){ 10*i + j }, 4, 3]).to eq [[11,12,13],[21,22,23],[31,32,33],[41,42,43]]
205
260
  end
206
261
 
207
- example "matrix_form table(lambda, n1, n2)" do
208
- pending "#matrix_form is yet to be implemented"
209
- expect(MLL::matrix_form MLL::table(->(i,j){ 10*i + j }, 4, 3)).to eq "
210
- "
211
- end
262
+ end
212
263
 
264
+ example "table(lambda, n1, min..max, [max, min, -step])" do
265
+ expect(table[->(i,j,k){ [i,j,k] }, 3, 2..3, [5, 1, -2]]).to be_a Array
266
+ expect(table[->(i,j,k){ [i,j,k] }, 3, 2..3, [5, 1, -2]]).to eq \
267
+ [
268
+ [[[1, 2, 5], [1, 2, 3], [1, 2, 1]], [[1, 3, 5], [1, 3, 3], [1, 3, 1]]],
269
+ [[[2, 2, 5], [2, 2, 3], [2, 2, 1]], [[2, 3, 5], [2, 3, 3], [2, 3, 1]]],
270
+ [[[3, 2, 5], [3, 2, 3], [3, 2, 1]], [[3, 3, 5], [3, 3, 3], [3, 3, 1]]]
271
+ ]
213
272
  end
214
273
 
215
274
  describe "Scope" do
216
275
 
217
276
  # TODO: "Make a triangular array:"
218
277
 
219
- example "table(lambda, [list1], [list2])" do
220
- expect(MLL::table(->(base, power){ base ** power }, [[1,2,4]], [[1,3,4]])).to be_a Array
221
- expect(MLL::table(->(base, power){ base ** power }, [[1,2,4]], [[1,3,4]])).to eq [[1,1,1],[2,8,16],[4,64,256]]
222
- expect(MLL::table(MLL::method(:plus), [[1]], [[2]])).to be_a Array
223
- expect(MLL::table(MLL::method(:plus), [[1]], [[2]])).to eq [[3]]
278
+ example "make an array from existing lists" do
279
+ expect(table[->(base, power){ base ** power }, [[1,2,4]], [[1,3,4]]]).to be_a Array
280
+ expect(table[->(base, power){ base ** power }, [[1,2,4]], [[1,3,4]]]).to eq [[1,1,1],[2,8,16],[4,64,256]]
281
+ expect(table[plus, [[1]], [[2]]]).to be_a Array
282
+ expect(table[plus, [[1]], [[2]]]).to eq [[3]]
224
283
  end
225
284
 
226
285
  end
@@ -228,8 +287,7 @@ describe MLL do
228
287
  describe "Applications" do
229
288
 
230
289
  example "column table(binomial, )" do
231
- pending "#binomial and #column are yet to be implemented"
232
- fail
290
+ skip "#binomial and #column are yet to be implemented"
233
291
  end
234
292
 
235
293
  end
@@ -241,66 +299,97 @@ describe MLL do
241
299
 
242
300
  # TODO take from docs more examples that involve other functions
243
301
 
244
- example "range( >3 args )" do
245
- expect{ MLL::range(1,2,3,4) }.to raise_error ArgumentError
302
+ example "no args raise ArgumentError" do
303
+ expect{ range[] }.to raise_error ArgumentError
304
+ end
305
+ example "more than 3 args raise ArgumentError" do
306
+ expect{ range[1,2,3,4] }.to raise_error ArgumentError
307
+ end
308
+ example "step equal to 0 raise ArgumentError" do
309
+ expect{ range[1,2,0] }.to raise_error ArgumentError
310
+ end
311
+
312
+ describe "Details" do
313
+
314
+ example "the arguments need not be integers" do
315
+ expect(range[0.25,2.9,1.25]).to be_a Enumerator
316
+ expect(range[0.25,2.9,1.25].to_a).to eq [0.25,1.5,2.75]
317
+ end
318
+
246
319
  end
247
320
 
248
321
  describe "Basic Examples" do
249
322
 
250
323
  example "range(n)" do
251
- expect(MLL::range(4)).to be_a Enumerator
252
- expect(MLL::range(4).to_a).to eq [1,2,3,4]
324
+ expect(range[4]).to be_a Enumerator
325
+ expect(range[4].to_a).to eq [1,2,3,4]
253
326
  end
254
- example "range(min, max)" do
255
- expect(MLL::range(2,5)).to be_a Enumerator
256
- expect(MLL::range(2,5).to_a).to eq [2,3,4,5]
327
+ example "range(n1, n2)" do
328
+ expect(range[2,5]).to be_a Enumerator
329
+ expect(range[2,5].to_a).to eq [2,3,4,5]
257
330
  end
258
331
  example "range(min, max, step)" do
259
- expect(MLL::range(1,2,3)).to be_a Enumerator
260
- expect(MLL::range(1,2,0.5).to_a).to eq [1,1.5,2] # can be precision problems
261
- expect(MLL::range(2,6,2).to_a).to eq [2,4,6]
262
- expect(MLL::range(-4,9,3).to_a).to eq [-4,-1,2,5,8]
263
- end
264
- example "range(max, min, -step)" do
265
- expect(MLL::range(10,-5,-2).to_a).to eq [10,8,6,4,2,0,-2,-4]
266
- expect(MLL::range(3,1,-1).to_a).to eq [3,2,1]
332
+ expect(range[1,2,0.5]).to be_a Enumerator
333
+ expect(range[1,2,0.5].to_a).to eq [1,1.5,2] # can be precision problems
334
+ expect(range[2,6,2].to_a).to eq [2,4,6]
335
+ expect(range[-4,9,3].to_a).to eq [-4,-1,2,5,8]
267
336
  end
268
337
 
269
338
  end
270
339
 
340
+ # NOTE: Wolfram Mathematica can't do this
341
+ example "range(max, min, -step)" do
342
+ expect(range[10,-5,-2]).to be_a Enumerator
343
+ expect(range[10,-5,-2].to_a).to eq [10,8,6,4,2,0,-2,-4]
344
+ expect(range[3,1,-1].to_a).to eq [3,2,1]
345
+ end
346
+
271
347
  describe "Generalizations & Extensions" do
272
348
 
273
- example "range([n1, n2, n3, n4])" do
274
- expect(MLL::range([5,2,6,3])).to be_a Enumerator
275
- MLL::range([5,2,6,3]).each do |i|
349
+ example "use a list of range specifications" do
350
+ expect(range[[5,2,6,3]]).to be_a Enumerator
351
+ range[[5,2,6,3]].each do |i|
276
352
  expect(i).to be_a Enumerator
277
353
  end
278
- expect(MLL::range([5,2,6,3]).to_a.map(&:to_a)).to eq [[1,2,3,4,5],[1,2],[1,2,3,4,5,6],[1,2,3]]
354
+ expect(range[[5,2,6,3]].to_a.map(&:to_a)).to eq [[1,2,3,4,5],[1,2],[1,2,3,4,5,6],[1,2,3]]
279
355
  end
280
356
 
281
357
  end
282
358
 
283
359
  describe "Applications" do
284
360
 
285
- example "power(n1, range(n2))" do
286
- pending "#power is yet to be implemented"
287
- expect(power(2, range(5))).to eq [2,4,8,16,32]
361
+ example "produce a geometric sequence" do
362
+ skip "#power is yet to be implemented"
363
+ expect(power[2, range[5]]).to be_a Enumerator
364
+ expect(power[2, range[5]].to_a).to eq [2,4,8,16,32]
288
365
  end
289
366
 
290
367
  end
291
368
 
292
- describe "Neat Examples" do
369
+ describe "Properties & Relations" do
293
370
 
294
- example "range(min..max)" do
295
- expect(MLL::range(1..3)).to be_a Enumerator
296
- MLL::range(1..3).each do |i|
297
- expect(i).to be_a Enumerator
298
- end
299
- expect(MLL::range(1..3).to_a.map(&:to_a)).to eq [[1],[1,2],[1,2,3]]
371
+ example "#range[imin,imax,di] is equivalent to #table[i,[imin,imax,di]]" do
372
+ expect(range[2,8,3].to_a).to eq table[->(i){ i }, [2,8,3]]
300
373
  end
301
- example "range(range(range(n)))" do
302
- # TODO do the same .tap thing in other type tests
303
- MLL::range(MLL::range(MLL::range(3))).tap do |o|
374
+
375
+ example "use #range or #span (;;) as #part specification" do
376
+ skip "#span and #part are yet to be implemented"
377
+ end
378
+
379
+ end
380
+
381
+ example "range(min..max)" do
382
+ expect(range[1..3]).to be_a Enumerator
383
+ range[1..3].each do |i|
384
+ expect(i).to be_a Enumerator
385
+ end
386
+ expect(range[1..3].to_a.map(&:to_a)).to eq [[1],[1,2],[1,2,3]]
387
+ end
388
+
389
+ describe "Neat Examples" do
390
+
391
+ example "make nested ranges" do
392
+ range[range[range[3]]].tap do |o|
304
393
  expect(o).to be_a Enumerator
305
394
  o.each do |i|
306
395
  expect(i).to be_a Enumerator
@@ -309,36 +398,64 @@ describe MLL do
309
398
  end
310
399
  end
311
400
  end
312
- expect(MLL::range(MLL::range(MLL::range(3))).to_a.map{ |i| i.to_a.map(&:to_a) }).to eq [[[1]],[[1],[1,2]],[[1],[1,2],[1,2,3]]]
401
+ expect(range[range[range[3]]].to_a.map{ |i| i.to_a.map(&:to_a) }).to eq [[[1]],[[1],[1,2]],[[1],[1,2],[1,2,3]]]
313
402
  end
314
403
 
315
- # TODO Nest[Range,3,6]
316
-
317
404
  end
318
405
 
319
- # TODO "Find an inverse permutation:"
320
- # TODO "Use Range or Span (;;) as Part specification:"
321
-
322
406
  end
323
407
 
324
408
  # http://reference.wolfram.com/language/ref/Subdivide.html
325
409
  describe "#subdivide" do
326
410
 
327
- example "subdivide(n)" do
328
- expect(MLL::subdivide(4)).to be_a Enumerator
329
- expect(MLL::subdivide(4).to_a).to eq [0,0.25,0.5,0.75,1]
411
+ example "no args raise ArgumentError" do
412
+ expect{ subdivide[] }.to raise_error ArgumentError
330
413
  end
331
- example "subdivide(max, n)" do
332
- expect(MLL::subdivide(10,5)).to be_a Enumerator
333
- expect(MLL::subdivide(10,5).to_a).to eq [0,2,4,6,8,10]
414
+ example "more than 3 args raise ArgumentError" do
415
+ expect{ subdivide[1,2,3,4] }.to raise_error ArgumentError
334
416
  end
335
- example "subdivide(min, max, n)" do
336
- expect(MLL::subdivide(-1,1,8)).to be_a Enumerator
337
- expect(MLL::subdivide(-1,1,8).to_a).to eq [-1,-0.75,-0.5,-0.25,0,0.25,0.5,0.75,1]
417
+ # example "division into 0 parts raises ArgumentError" do
418
+ # expect{ subdivide[0] }.to raise_error ArgumentError
419
+ # end
420
+
421
+ describe "Details" do
422
+
423
+ example "generates a list of length n+1" do
424
+ expect(subdivide[5]).to be_a Enumerator
425
+ expect(subdivide[5].size).to eq 6
426
+ end
427
+
338
428
  end
339
- example "subdivide(max, min, n)" do
340
- expect(MLL::subdivide(1,-1,8)).to be_a Enumerator
341
- expect(MLL::subdivide(1,-1,8).to_a).to eq [1,0.75,0.5,0.25,0,-0.25,-0.5,-0.75,-1]
429
+
430
+ describe "Basic Examples" do
431
+
432
+ example "subdivide the unit interval into 4 equal parts" do
433
+ expect(subdivide[4]).to be_a Enumerator
434
+ expect(subdivide[4].to_a).to eq [0,0.25,0.5,0.75,1]
435
+ end
436
+ example "subdivide the interval 0 to 10 into 5 equal parts" do
437
+ expect(subdivide[10,5]).to be_a Enumerator
438
+ expect(subdivide[10,5].to_a).to eq [0,2,4,6,8,10]
439
+ end
440
+ example "subdivide the interval -1 to 1 into 8 equal parts using machine precision" do
441
+ expect(subdivide[-1,1,8]).to be_a Enumerator
442
+ expect(subdivide[-1,1,8].to_a).to eq [-1,-0.75,-0.5,-0.25,0,0.25,0.5,0.75,1]
443
+ end
444
+
445
+ # NOTE: Wolfram Mathematica can't do this
446
+ example "subdivide(max, min, n)" do
447
+ expect(subdivide[1,-1,8]).to be_a Enumerator
448
+ expect(subdivide[1,-1,8].to_a).to eq [1,0.75,0.5,0.25,0,-0.25,-0.5,-0.75,-1]
449
+ end
450
+
451
+ end
452
+
453
+ describe "Properties & Relations" do
454
+
455
+ example "subdivide[xmin,xmax,n] is equivalent to xmin+(xmax-xmin)Range[0,n]/n" do
456
+ expect(subdivide[2,10,4].to_a).to eq plus[2,divide[times[10-2,range[0,4]],4]].to_a
457
+ end
458
+
342
459
  end
343
460
 
344
461
  end
@@ -346,16 +463,15 @@ describe MLL do
346
463
  # http://reference.wolfram.com/language/ref/NestList.html
347
464
  describe "#nest_list" do
348
465
 
349
- example "#nest_list gives a list of length n+1" do
350
- expect(MLL::nest_list(->{}, 0, 5)).to be_a Enumerator
351
- expect(MLL::nest_list(->(*args){}, 0, 5).to_a.size).to eq 6
466
+ example "gives a list of length n+1" do
467
+ expect(nest_list[->{}, 0, 5]).to be_a Enumerator
468
+ expect(nest_list[->(*args){}, 0, 5].to_a.size).to eq 6
352
469
  end
353
470
 
354
471
  describe "Basic Examples" do
355
472
 
356
- example "'nest_list with #cos starting with 1.0)'" do
357
- pending "#cos is yet to be implemented"
358
- fail
473
+ example "???" do
474
+ skip "#cos (or #sqrt or anything unar is yet to be implemented"
359
475
  end
360
476
 
361
477
  end
@@ -363,8 +479,7 @@ describe MLL do
363
479
  describe "Scope" do
364
480
 
365
481
  example "nesting can return a single number" do
366
- pending "#sqrt is yet to be implemented"
367
- fail
482
+ skip "#sqrt is yet to be implemented"
368
483
  end
369
484
 
370
485
  end
@@ -372,39 +487,41 @@ describe MLL do
372
487
  describe "Applications" do
373
488
 
374
489
  example "powers of 2" do
375
- expect(MLL::nest_list(->(i){ 2*i }, 1, 10)).to be_a Enumerator
376
- expect(MLL::nest_list(->(i){ 2*i }, 1, 10).to_a).to eq [1,2,4,8,16,32,64,128,256,512,1024]
490
+ expect(nest_list[->(i){ 2*i }, 1, 10]).to be_a Enumerator
491
+ expect(nest_list[->(i){ 2*i }, 1, 10].to_a).to eq [1,2,4,8,16,32,64,128,256,512,1024]
377
492
  end
378
493
  example "iterates in the problem" do
379
- expect(MLL::nest_list(->(i){ i.even? ? i/2 : (i*3+1)/2 }, 100, 20)).to be_a Enumerator
380
- expect(MLL::nest_list(->(i){ i.even? ? i/2 : (i*3+1)/2 }, 100, 20).to_a).to eq [100,50,25,38,19,29,44,22,11,17,26,13,20,10,5,8,4,2,1,2,1]
494
+ expect(nest_list[->(i){ i.even? ? i/2 : (i*3+1)/2 }, 100, 20]).to be_a Enumerator
495
+ expect(nest_list[->(i){ i.even? ? i/2 : (i*3+1)/2 }, 100, 20].to_a).to eq [100,50,25,38,19,29,44,22,11,17,26,13,20,10,5,8,4,2,1,2,1]
381
496
  end
382
497
  example "linear congruential pseudorandom generator" do
383
- expect(MLL::nest_list(->(i){ (i*59)%101 }, 1, 15)).to be_a Enumerator
384
- expect(MLL::nest_list(->(i){ (i*59)%101 }, 1, 15).to_a).to eq [1,59,47,46,88,41,96,8,68,73,65,98,25,61,64,39]
498
+ expect(nest_list[->(i){ (i*59)%101 }, 1, 15]).to be_a Enumerator
499
+ expect(nest_list[->(i){ (i*59)%101 }, 1, 15].to_a).to eq [1,59,47,46,88,41,96,8,68,73,65,98,25,61,64,39]
385
500
  end
386
501
  example "random walk" do
387
- expect(( r = Random.new(0); MLL::nest_list(->(i){ i+[-1,1][r.rand(2)] }, 0, 20) )).to be_a Enumerator
388
- expect(( r = Random.new(0); MLL::nest_list(->(i){ i+[-1,1][r.rand(2)] }, 0, 20).to_a )).to eq [0,-1,0,1,0,1,2,3,4,5,6,7,6,5,6,5,4,3,2,1,2]
502
+ expect(( r = Random.new(0); nest_list[->(i){ i+[-1,1][r.rand(2)] }, 0, 20] )).to be_a Enumerator
503
+ expect(( r = Random.new(0); nest_list[->(i){ i+[-1,1][r.rand(2)] }, 0, 20].to_a )).to eq [0,-1,0,1,0,1,2,3,4,5,6,7,6,5,6,5,4,3,2,1,2]
389
504
  end
390
505
  example "successively rotate a list" do
391
- expect(MLL::nest_list(->(i){ i.rotate 1 }, [1,2,3,4], 4)).to be_a Enumerator
392
- expect(MLL::nest_list(->(i){ i.rotate 1 }, [1,2,3,4], 4).to_a).to eq [[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3],[1,2,3,4]]
506
+ expect(nest_list[->(i){ i.rotate 1 }, [1,2,3,4], 4]).to be_a Enumerator
507
+ expect(nest_list[->(i){ i.rotate 1 }, [1,2,3,4], 4].to_a).to eq [[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3],[1,2,3,4]]
393
508
  end
394
509
 
395
510
  end
396
511
 
397
512
  describe "Properties & Relations" do
398
513
 
399
- # TODO "Nest gives the last element of NestList:"
514
+ example "#nest gives the last element of #nest_list" do
515
+ expect(nest_list[->(i){ i*2 }, 3, 4].to_a.last).to eq nest[->(i){ i*2 }, 3, 4]
516
+ end
400
517
 
401
518
  example "nesting zero times simply returns to the original argument" do
402
- expect(MLL::nest_list(->{fail}, 5, 0)).to be_a Enumerator
403
- expect(MLL::nest_list(->(*args){fail}, 5, 0).to_a).to eq [5]
519
+ expect(nest_list[->{fail}, 5, 0]).to be_a Enumerator
520
+ expect(nest_list[->(*args){fail}, 5, 0].to_a).to eq [5]
404
521
  end
405
522
  example "#fold_list automatically inserts second arguments from a list" do
406
- expect(MLL::nest_list(->(i ){ i*2 }, 3, 4).to_a).to eq \
407
- MLL::fold_list(->(i,j){ i*j }, 3, [2]*4).to_a
523
+ expect(nest_list[->(i ){ i*2 }, 3, 4].to_a).to eq \
524
+ fold_list[->(i,j){ i*j }, 3, [2]*4].to_a
408
525
  end
409
526
 
410
527
  end
@@ -416,69 +533,78 @@ describe MLL do
416
533
  # http://reference.wolfram.com/language/guide/ApplyingFunctionsToLists.html
417
534
  describe "Applying Functions to Lists" do
418
535
 
536
+ # http://reference.wolfram.com/language/ref/FoldList.html
419
537
  describe "#fold_list" do
420
538
 
539
+ describe "Details" do
540
+
541
+ example "with a length n list, #fold_list generates a list of length n+1" do
542
+ skip "waiting got Wolfram team to fix the reported bug"
543
+ end
544
+ example "#fold_list[f,list] is equivalent to #fold_list[f,[list][0],list[1..-1]]" do
545
+ expect(fold_list[plus,[1,2,3]]).to be_a Enumerator
546
+ expect(fold_list[plus,[1,2,3]].to_a).to eq fold_list[plus,1,[2,3]].to_a
547
+ end
548
+
549
+ end
550
+
421
551
  describe "Basic Examples" do
422
552
 
423
- example "fold_list(function, list)" do
424
- expect(MLL::fold_list(MLL.method(:plus),[1,2,3,4])).to be_a Enumerator
425
- expect(MLL::fold_list(MLL.method(:plus),[1,2,3,4]).to_a).to eq [1,3,6,10]
553
+ example "cumulative sums of the elements of the list" do
554
+ expect(fold_list[plus,5,[1,2,3,4]]).to be_a Enumerator
555
+ expect(fold_list[plus,5,[1,2,3,4]].to_a).to eq [5,6,8,11,15]
426
556
  end
427
- example "fold_list(function, n, list)" do
428
- expect(MLL::fold_list(MLL.method(:plus),5,[1,2,3,4])).to be_a Enumerator
429
- expect(MLL::fold_list(MLL.method(:plus),5,[1,2,3,4]).to_a).to eq [5,6,8,11,15]
557
+ example "cumulative powers" do
558
+ expect(fold_list[->(base, power){ base ** power },2,[3,2,1]]).to be_a Enumerator
559
+ expect(fold_list[->(base, power){ base ** power },2,[3,2,1]].to_a).to eq [2,8,64,64]
430
560
  end
431
- example "fold_list(lambda, n, list)" do
432
- expect(MLL::fold_list(->(base, power){ base ** power },2,[3,2,1])).to be_a Enumerator
433
- expect(MLL::fold_list(->(base, power){ base ** power },2,[3,2,1]).to_a).to eq [2,8,64,64]
561
+ example "start from the first element of the list" do
562
+ expect(fold_list[plus,[1,2,3,4]]).to be_a Enumerator
563
+ expect(fold_list[plus,[1,2,3,4]].to_a).to eq [1,3,6,10]
434
564
  end
435
565
 
436
566
  end
437
567
 
438
568
  describe "Applications" do
439
569
 
440
- example "fold_list(function, n, list)" do
441
- expect(MLL::fold_list(MLL.method(:times), [*1..10])).to be_a Enumerator
442
- expect(MLL::fold_list(MLL.method(:times), [*1..10]).to_a).to eq [1,2,6,24,120,720,5040,40320,362880,3628800]
443
- end
444
- example "fold_list(lambda, n, list)" do
445
- expect(MLL::fold_list(->(a,b){ 10*a + b }, 0, [4,5,1,6,7,8])).to be_a Enumerator
446
- expect(MLL::fold_list(->(a,b){ 10*a + b }, 0, [4,5,1,6,7,8]).to_a).to eq [0,4,45,451,4516,45167,451678]
447
- end
448
- example "fold_list(lambda, n, list)" do
449
- expect(( r = Random.new(0); MLL::fold_list(MLL.method(:plus), 0, Array.new(20){ [-1,1][r.rand(2)] }) )).to be_a Enumerator
450
- expect(( r = Random.new(0); MLL::fold_list(MLL.method(:plus), 0, Array.new(20){ [-1,1][r.rand(2)] }).to_a )).to eq [0,-1,0,1,0,1,2,3,4,5,6,7,6,5,6,5,4,3,2,1,2]
570
+ # TODO maybe move it to README.md
571
+ example "generate a random walk" do
572
+ expect(( r = Random.new(0); fold_list[plus, 0, Array.new(20){ [-1,1][r.rand(2)] }] )).to be_a Enumerator
573
+ expect(( r = Random.new(0); fold_list[plus, 0, Array.new(20){ [-1,1][r.rand(2)] }].to_a )).to eq [0,-1,0,1,0,1,2,3,4,5,6,7,6,5,6,5,4,3,2,1,2]
451
574
  end
452
575
 
453
- # TODO "Find successively deeper parts in an expression:"
576
+ example "find successively deeper parts in an expression" do
577
+ expect(fold_list[->(list,index){ list[index] }, [[1,2],[3,[4,5],6,7],8], [1,1,0]]).to be_a Enumerator
578
+ expect(fold_list[->(list,index){ list[index] }, [[1,2],[3,[4,5],6,7],8], [1,1,0]].to_a).to eq [[[1,2],[3,[4,5],6,7],8],[3,[4,5],6,7],[4,5],4]
579
+ end
454
580
 
455
581
  end
456
582
 
457
583
  describe "Properties & Relations" do
458
584
 
459
- example "#fold_list makes a list of length n+1" do
460
- expect(MLL::fold_list(->{}, 0, [*1..10])).to be_a Enumerator
461
- expect(MLL::fold_list(->(*args){}, 0, [*1..10]).to_a.size).to eq 11
585
+ example "makes a list of length n+1" do
586
+ expect(fold_list[->{}, 0, [*1..10]]).to be_a Enumerator
587
+ expect(fold_list[->(*args){}, 0, [*1..10]].to_a.size).to eq 11
462
588
  end
463
589
  example "folding with an empty list does not apply the function at all" do
464
- expect(MLL::fold_list(->{}, 0, [])).to be_a Enumerator
465
- expect(MLL::fold_list(->(*args){}, 0, []).to_a).to eq [0]
590
+ expect(fold_list[->{}, 0, []]).to be_a Enumerator
591
+ expect(fold_list[->(*args){}, 0, []].to_a).to eq [0]
466
592
  end
467
593
  example "Ruby#inject gives the last element of #fold_list" do
468
594
  f = ->(i,j){ i+j }
469
- expect(MLL::fold_list(f, [1,2,3])).to be_a Enumerator
470
- expect(MLL::fold_list(f, [1,2,3]).to_a.last).to eq [1,2,3].inject(&f)
595
+ expect(fold_list[f, [1,2,3]]).to be_a Enumerator
596
+ expect(fold_list[f, [1,2,3]].to_a.last).to eq [1,2,3].inject(&f)
597
+ end
598
+ example "functions that ignore their second argument give the same result as in #nest_list" do
599
+ expect(fold_list[->(i,_){ [i] }, 0, range[5]].to_a).to eq nest_list[->(i){ [i] }, 0, 5].to_a
471
600
  end
472
- # TODO "Functions that ignore their second argument give the same result as in NestList:"
473
- # TODO "Accumulate is equivalent to FoldList with Plus:"
474
601
 
475
602
  end
476
603
 
477
604
  describe "Neat Examples" do
478
605
 
479
606
  example "compute the minimum number of coins of different value needed to make up an amount" do
480
- pending "at least #mod is yet to be implemented"
481
- fail
607
+ skip "at least #mod is yet to be implemented"
482
608
  end
483
609
 
484
610
  end
@@ -501,12 +627,13 @@ describe MLL do
501
627
 
502
628
  # TODO move #nest_list and #fold_list and others here?
503
629
 
630
+ # http://reference.wolfram.com/language/ref/Nest.html
504
631
  describe "#nest" do
505
632
 
506
633
  describe "Basic Examples" do
507
634
 
508
635
  example "the function to nest can be a pure function" do
509
- expect(MLL::nest(->(i){ (1+i)**2 }, 1, 3)).to eq 676
636
+ expect(nest[->(i){ (1+i)**2 }, 1, 3]).to eq 676
510
637
  end
511
638
 
512
639
  end
@@ -514,8 +641,7 @@ describe MLL do
514
641
  describe "Scope" do
515
642
 
516
643
  example "nesting can return a single number" do
517
- pending "#sqrt is yet to be implemented"
518
- fail
644
+ skip "#sqrt is yet to be implemented"
519
645
  end
520
646
 
521
647
  end
@@ -523,21 +649,18 @@ describe MLL do
523
649
  describe "Applications" do
524
650
 
525
651
  example "newton iterations for" do
526
- pending "need to deal with Rationals first"
527
- fail
652
+ skip "waiting for Rationals to be used"
528
653
  end
529
654
  example "consecutive pairs of Fibonacci numbers" do
530
- pending "implement #dot ?"
531
- fail
655
+ skip "#dot is yet to be implemented"
532
656
  end
533
657
 
534
658
  end
535
659
 
536
660
  describe "Properties & Relations" do
537
661
 
538
- example "#fold automatically inserts second arguments from a list" do
539
- expect(MLL::nest(->(i){ i*2 }, 3, 4)).to eq \
540
- ([2]*4).inject(3){ |i,j| i*j }
662
+ example "Ruby#inject automatically inserts second arguments from a list" do
663
+ expect(nest[->(i){ i*2 }, 3, 4]).to eq ([2]*4).inject(3){ |i,j| i*j }
541
664
  end
542
665
 
543
666
  end
@@ -557,6 +680,7 @@ end
557
680
 
558
681
  __END__
559
682
 
560
- Table Array
561
- Times Product
562
- Plus Total Sum?
683
+ Table Array
684
+ Times Product
685
+ Plus Total Sum?
686
+ NestList Accumulate