musa-dsl 0.22.5 → 0.23.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/lib/musa-dsl.rb +14 -8
  4. data/lib/musa-dsl/core-ext/deep-copy.rb +12 -1
  5. data/lib/musa-dsl/core-ext/inspect-nice.rb +1 -2
  6. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +13 -11
  7. data/lib/musa-dsl/datasets/p.rb +38 -15
  8. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +14 -12
  9. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +32 -6
  10. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +24 -10
  11. data/lib/musa-dsl/generative/backboner.rb +6 -11
  12. data/lib/musa-dsl/generative/generative-grammar.rb +1 -3
  13. data/lib/musa-dsl/generative/markov.rb +10 -6
  14. data/lib/musa-dsl/logger/logger.rb +6 -1
  15. data/lib/musa-dsl/midi/midi-voices.rb +8 -7
  16. data/lib/musa-dsl/music/scales.rb +1 -1
  17. data/lib/musa-dsl/neumalang/neumalang.rb +1 -1
  18. data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
  19. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +2 -2
  20. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +2 -1
  21. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +2 -0
  22. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +4 -2
  23. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +6 -6
  24. data/lib/musa-dsl/series/base-series.rb +293 -144
  25. data/lib/musa-dsl/series/buffer-serie.rb +236 -0
  26. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +145 -115
  27. data/lib/musa-dsl/series/main-serie-constructors.rb +249 -156
  28. data/lib/musa-dsl/series/main-serie-operations.rb +331 -318
  29. data/lib/musa-dsl/series/proxy-serie.rb +25 -41
  30. data/lib/musa-dsl/series/quantizer-serie.rb +38 -38
  31. data/lib/musa-dsl/series/queue-serie.rb +39 -43
  32. data/lib/musa-dsl/series/series-composer.rb +316 -0
  33. data/lib/musa-dsl/series/series.rb +5 -1
  34. data/lib/musa-dsl/series/timed-serie.rb +119 -130
  35. data/musa-dsl.gemspec +13 -3
  36. metadata +9 -9
  37. data/.ruby-version +0 -1
  38. data/lib/musa-dsl/series/holder-serie.rb +0 -87
@@ -1,6 +1,6 @@
1
1
  module Musa
2
2
  module Series
3
- module SerieOperations
3
+ module Operations
4
4
  def autorestart
5
5
  Autorestart.new self
6
6
  end
@@ -108,12 +108,16 @@ module Musa
108
108
 
109
109
  alias_method :eval, :with
110
110
 
111
- def map(&yield_block)
112
- ProcessWith.new self, &yield_block
111
+ def map(&block)
112
+ ProcessWith.new self, &block
113
113
  end
114
114
 
115
- def anticipate(&yield_block)
116
- Anticipate.new self, &yield_block
115
+ def anticipate(&block)
116
+ Anticipate.new self, &block
117
+ end
118
+
119
+ def lazy(&block)
120
+ LazySerieEval.new self, &block
117
121
  end
118
122
 
119
123
  ###
@@ -121,34 +125,36 @@ module Musa
121
125
  ###
122
126
 
123
127
  class ProcessWith
124
- include Musa::Extension::SmartProcBinder
125
- include Serie
126
-
127
- attr_reader :source, :on_restart, :block
128
- def with_sources; @sources; end
128
+ include Serie.with(source: true, sources: true, sources_as: :with_sources, smart_block: true)
129
129
 
130
130
  def initialize(serie, with_series = nil, on_restart = nil, &block)
131
- @source = serie
132
- @sources = with_series || {}
133
- @on_restart = on_restart
134
- @block = SmartProcBinder.new(block) if block_given?
131
+ self.source = serie
132
+ self.with_sources = with_series || {}
133
+ self.on_restart = on_restart
134
+ self.proc = block if block
135
135
 
136
- if @source.prototype?
137
- @sources = @sources.transform_values { |s| s.prototype }.freeze
136
+ init
137
+ end
138
+
139
+ def on_restart(&block)
140
+ if block
141
+ @on_restart = block
138
142
  else
139
- @sources = @sources.transform_values { |s| s.instance }
143
+ @on_restart
140
144
  end
145
+ end
141
146
 
142
- mark_regarding! @source
147
+ def on_restart=(block)
148
+ @on_restart = block
143
149
  end
144
150
 
145
- def _restart
151
+ private def _restart
146
152
  @source.restart
147
- @sources.values.each { |s| s.restart }
153
+ @sources.values.each(&:restart)
148
154
  @on_restart.call if @on_restart
149
155
  end
150
156
 
151
- def _next_value
157
+ private def _next_value
152
158
  main = @source.next_value
153
159
  others = @sources.transform_values { |v| v.next_value }
154
160
 
@@ -173,27 +179,29 @@ module Musa
173
179
  private_constant :ProcessWith
174
180
 
175
181
  class Anticipate
176
- include Musa::Extension::SmartProcBinder
177
- include Serie
178
-
179
- attr_reader :source, :block
182
+ include Serie.with(source: true, block: true)
180
183
 
181
184
  def initialize(serie, &block)
182
- @source = serie
183
- @block = block
185
+ self.source = serie
186
+ self.proc = block
184
187
 
185
- mark_regarding! @source
188
+ init
186
189
  end
187
190
 
188
- def _restart
191
+ private def _restart
189
192
  @source.restart
190
193
  end
191
194
 
192
- def _next_value
195
+ private def _next_value
196
+ previous_value = @source.current_value
193
197
  value = @source.next_value
194
198
  peek_next_value = @source.peek_next_value
195
199
 
196
- @block.call(value, peek_next_value)
200
+ if value.nil?
201
+ nil
202
+ else
203
+ @block.call(previous_value, value, peek_next_value)
204
+ end
197
205
  end
198
206
 
199
207
  def infinite?
@@ -204,30 +212,16 @@ module Musa
204
212
  private_constant :Anticipate
205
213
 
206
214
  class Switcher
207
- include Serie
208
-
209
- attr_accessor :sources
210
- def selector; @source; end
215
+ include Serie.with(source: true, sources: true, sources_as: :options)
211
216
 
212
217
  def initialize(selector, indexed_series, hash_series)
218
+ self.source = selector
219
+ self.options = indexed_series || hash_series
213
220
 
214
- @source = selector
215
- get = @source.prototype? ? :prototype : :instance
216
-
217
- if indexed_series && !indexed_series.empty?
218
- @sources = indexed_series.collect(&get)
219
- elsif hash_series && !hash_series.empty?
220
- @sources = hash_series.clone.transform_values(&get)
221
- end
222
-
223
- if get == :prototype!
224
- @sources.freeze
225
- end
226
-
227
- mark_regarding! @source
221
+ init
228
222
  end
229
223
 
230
- def _restart
224
+ private def _restart
231
225
  @source.restart
232
226
  if @sources.is_a? Array
233
227
  @sources.each(&:restart)
@@ -236,7 +230,7 @@ module Musa
236
230
  end
237
231
  end
238
232
 
239
- def _next_value
233
+ private def _next_value
240
234
  value = nil
241
235
 
242
236
  index_or_key = @source.next_value
@@ -254,46 +248,31 @@ module Musa
254
248
  private_constant :Switcher
255
249
 
256
250
  class MultiplexSelector
257
- include Serie
258
-
259
- def selector; @source; end
260
- attr_accessor :sources
251
+ include Serie.with(source: true, sources: true, sources_as: :options)
261
252
 
262
253
  def initialize(selector, indexed_series, hash_series)
263
- @source = selector
264
- get = @source.prototype? ? :prototype : :instance
265
-
266
- if indexed_series && !indexed_series.empty?
267
- @sources = indexed_series.collect(&get)
268
- elsif hash_series && !hash_series.empty?
269
- @sources = hash_series.clone.transform_values(&get)
270
- end
271
-
272
- _restart false
254
+ self.source = selector
255
+ self.options = indexed_series || hash_series
273
256
 
274
- if get == :prototype
275
- @sources.freeze
276
- end
277
-
278
- mark_regarding! @source
257
+ init
279
258
  end
280
259
 
281
- def _restart(restart_sources = true)
260
+ private def _init
282
261
  @current_value = nil
262
+ @first = true
263
+ end
283
264
 
284
- if restart_sources
285
- @source.restart
286
- if @sources.is_a? Array
287
- @sources.each(&:restart)
288
- elsif @sources.is_a? Hash
289
- @sources.each { |_key, serie| serie.restart }
290
- end
291
- end
265
+ private def _restart
266
+ @source.restart
292
267
 
293
- @first = true
268
+ if @sources.is_a? Array
269
+ @sources.each(&:restart)
270
+ elsif @sources.is_a? Hash
271
+ @sources.values.each(&:restart)
272
+ end
294
273
  end
295
274
 
296
- def _next_value
275
+ private def _next_value
297
276
  @current_value =
298
277
  if @first || !@current_value.nil?
299
278
  @first = false
@@ -313,34 +292,21 @@ module Musa
313
292
  private_constant :MultiplexSelector
314
293
 
315
294
  class SwitchFullSerie
316
- include Serie
317
-
318
- def selector; @source; end
319
- attr_accessor :sources
295
+ include Serie.with(source: true, sources: true, sources_as: :options)
320
296
 
321
297
  def initialize(selector, indexed_series, hash_series)
322
- @source = selector
323
- get = @source.prototype? ? :prototype : :instance
324
-
325
- if indexed_series && !indexed_series.empty?
326
- @sources = indexed_series.collect(&get)
327
- elsif hash_series && !hash_series.empty?
328
- @sources = hash_series.clone.transform_values(&get)
329
- end
330
-
331
- if get == :prototype
332
- @sources.freeze
333
- end
298
+ self.source = selector
299
+ self.options = indexed_series || hash_series
334
300
 
335
- mark_regarding! @source
301
+ init
336
302
  end
337
303
 
338
- def _restart
304
+ private def _restart
339
305
  @source.restart
340
306
  @sources.each(&:restart)
341
307
  end
342
308
 
343
- def _next_value
309
+ private def _next_value
344
310
  value = nil
345
311
 
346
312
  value = @sources[@index_or_key].next_value unless @index_or_key.nil?
@@ -362,21 +328,18 @@ module Musa
362
328
  private_constant :SwitchFullSerie
363
329
 
364
330
  class InfiniteRepeater
365
- include Serie
366
-
367
- attr_reader :source
331
+ include Serie.with(source: true)
368
332
 
369
333
  def initialize(serie)
370
- @source = serie
371
-
372
- mark_regarding! @source
334
+ self.source = serie
335
+ init
373
336
  end
374
337
 
375
- def _restart
338
+ private def _restart
376
339
  @source.restart
377
340
  end
378
341
 
379
- def _next_value
342
+ private def _next_value
380
343
  value = @source.next_value
381
344
 
382
345
  if value.nil?
@@ -395,38 +358,47 @@ module Musa
395
358
  private_constant :InfiniteRepeater
396
359
 
397
360
  class Repeater
398
- include Serie
399
-
400
- attr_reader :source, :times, :condition
361
+ include Serie.with(source: true)
401
362
 
402
363
  def initialize(serie, times = nil, &condition)
403
- @source = serie
364
+ self.source = serie
365
+ self.times = times
366
+ self.condition = condition
404
367
 
405
- @times = times
406
- @external_condition = condition
368
+ init
369
+ end
407
370
 
408
- _restart false
409
- @condition = calculate_condition
371
+ attr_reader :times
410
372
 
411
- mark_regarding! @source
373
+ def times=(value)
374
+ @times = value
375
+ calculate_condition
412
376
  end
413
377
 
414
- def _prototype!
415
- super
416
- @condition = calculate_condition
378
+ def condition(&block)
379
+ if block
380
+ @external_condition = block
381
+ calculate_condition
382
+ else
383
+ @external_condition
384
+ end
417
385
  end
418
386
 
419
- def _instance!
420
- super
421
- @condition = calculate_condition
387
+ def condition=(block)
388
+ @external_condition = block
389
+ calculate_condition
422
390
  end
423
391
 
424
- def _restart(restart_sources = true)
392
+ private def _init
425
393
  @count = 0
426
- @source.restart if restart_sources
394
+ calculate_condition
395
+ end
396
+
397
+ private def _restart
398
+ @source.restart
427
399
  end
428
400
 
429
- def _next_value
401
+ private def _next_value
430
402
  value = @source.next_value
431
403
 
432
404
  if value.nil?
@@ -446,38 +418,39 @@ module Musa
446
418
  end
447
419
 
448
420
  private def calculate_condition
449
- if @external_condition
450
- @external_condition
451
- elsif @times
452
- proc { @count < @times }
453
- else
454
- proc { false }
455
- end
421
+ @condition = if @external_condition
422
+ @external_condition
423
+ elsif @times
424
+ proc { @count < @times }
425
+ else
426
+ proc { false }
427
+ end
456
428
  end
457
429
  end
458
430
 
459
431
  private_constant :Repeater
460
432
 
461
433
  class LengthLimiter
462
- include Serie
463
-
464
- attr_reader :source, :length
434
+ include Serie.with(source: true)
465
435
 
466
436
  def initialize(serie, length)
467
- @source = serie
468
- @length = length
469
-
470
- _restart false
437
+ self.source = serie
438
+ self.length = length
471
439
 
472
- mark_regarding! @source
440
+ init
473
441
  end
474
442
 
475
- def _restart(restart_sources = true)
443
+ attr_accessor :length
444
+
445
+ private def _init
476
446
  @position = 0
477
- @source.restart if restart_sources
478
447
  end
479
448
 
480
- def _next_value
449
+ private def _restart
450
+ @source.restart
451
+ end
452
+
453
+ private def _next_value
481
454
  if @position < @length
482
455
  @position += 1
483
456
  @source.next_value
@@ -492,25 +465,26 @@ module Musa
492
465
  private_constant :LengthLimiter
493
466
 
494
467
  class Skipper
495
- include Serie
496
-
497
- attr_reader :source, :length
468
+ include Serie.with(source: true)
498
469
 
499
470
  def initialize(serie, length)
500
- @source = serie
501
- @length = length
502
-
503
- _restart false
471
+ self.source = serie
472
+ self.length = length
504
473
 
505
- mark_regarding! @source
474
+ init
506
475
  end
507
476
 
508
- def _restart(restart_sources = true)
509
- @source.restart if restart_sources
477
+ attr_accessor :length
478
+
479
+ private def _init
510
480
  @first = true
511
481
  end
512
482
 
513
- def _next_value
483
+ private def _restart
484
+ @source.restart
485
+ end
486
+
487
+ private def _next_value
514
488
  @length.times { @source.next_value } if @first
515
489
  @first = nil
516
490
 
@@ -525,40 +499,25 @@ module Musa
525
499
  private_constant :Skipper
526
500
 
527
501
  class Flattener
528
- include Serie
529
-
530
- attr_reader :source
502
+ include Serie.base
531
503
 
532
504
  def initialize(serie)
533
505
  @source = serie
534
-
535
- _restart false
536
-
537
506
  mark_regarding! @source
507
+ init
538
508
  end
539
509
 
540
- def _prototype!
541
- super
542
- _restart false
543
- end
544
-
545
- def _instance!
546
- super
547
- _restart false
510
+ private def _init
511
+ @stack = [@source]
512
+ @restart_each_serie = false
548
513
  end
549
514
 
550
- def _restart(restart_sources = true)
551
- if restart_sources
552
- @source.restart
553
- @restart_each_serie = true
554
- else
555
- @restart_each_serie = false
556
- end
557
-
558
- @stack = [@source]
515
+ private def _restart
516
+ @source.restart
517
+ @restart_each_serie = true
559
518
  end
560
519
 
561
- def _next_value
520
+ private def _next_value
562
521
  if @stack.last
563
522
  value = @stack.last.next_value
564
523
 
@@ -585,26 +544,24 @@ module Musa
585
544
  private_constant :Flattener
586
545
 
587
546
  class MergeSerieOfSeries
588
- include Serie
589
-
590
- attr_reader :source
547
+ include Serie.with(source: true)
591
548
 
592
549
  def initialize(serie)
593
- @source = serie
594
- _restart false
595
-
596
- mark_regarding! @source
550
+ self.source = serie
551
+ init
597
552
  end
598
553
 
599
- def _restart(restart_sources = true)
600
- if restart_sources
601
- @source.restart
602
- @restart_each_serie = true
603
- end
554
+ private def _init
604
555
  @current_serie = nil
556
+ @restart_each_serie = false
605
557
  end
606
558
 
607
- def _next_value
559
+ private def _restart
560
+ @source.restart
561
+ @restart_each_serie = true
562
+ end
563
+
564
+ private def _next_value
608
565
  value = nil
609
566
 
610
567
  restart_current_serie_if_needed = false
@@ -641,27 +598,28 @@ module Musa
641
598
  private_constant :MergeSerieOfSeries
642
599
 
643
600
  class Processor
644
- include Musa::Extension::SmartProcBinder
645
- include Serie
646
-
647
- attr_reader :source
601
+ include Serie.with(source: true, smart_block: true)
648
602
 
649
603
  def initialize(serie, parameters, &processor)
650
- @source = serie
651
- @parameters = parameters
652
- @processor = SmartProcBinder.new(processor)
604
+ self.source = serie
653
605
 
654
- _restart false
606
+ self.parameters = parameters
607
+ self.proc = processor if processor
655
608
 
656
- mark_regarding! @source
609
+ init
657
610
  end
658
611
 
659
- def _restart(restart_source = true)
660
- @source.restart if restart_source
612
+ attr_accessor :parameters
613
+
614
+ private def _init
661
615
  @pending_values = []
662
616
  end
663
617
 
664
- def _next_value
618
+ private def _restart
619
+ @source.restart
620
+ end
621
+
622
+ private def _next_value
665
623
  if @pending_values.empty?
666
624
 
667
625
  v = @source.next_value
@@ -669,7 +627,7 @@ module Musa
669
627
  if v.nil?
670
628
  nil
671
629
  else
672
- value = @processor.call(v, **@parameters)
630
+ value = @block.call(v, **@parameters)
673
631
 
674
632
  if value.is_a?(Array)
675
633
  @pending_values = value
@@ -695,23 +653,22 @@ module Musa
695
653
  end
696
654
 
697
655
  class Autorestart
698
- include Serie
699
-
700
- attr_reader :source
656
+ include Serie.with(source: true)
701
657
 
702
658
  def initialize(serie)
703
- @source = serie
659
+ self.source = serie
660
+ init
661
+ end
704
662
 
663
+ private def _init
705
664
  @restart_on_next = false
706
-
707
- mark_regarding! @source
708
665
  end
709
666
 
710
- def _restart
667
+ private def _restart
711
668
  @source.restart
712
669
  end
713
670
 
714
- def _next_value
671
+ private def _next_value
715
672
  if @restart_on_next
716
673
  @source.restart
717
674
  @restart_on_next = false
@@ -728,53 +685,59 @@ module Musa
728
685
  private_constant :Autorestart
729
686
 
730
687
  class Cutter
731
- include Serie
732
-
733
- attr_reader :source, :length
688
+ include Serie.with(source: true)
734
689
 
735
690
  def initialize(serie, length)
736
- @source = serie
737
- @length = length
691
+ self.source = serie
692
+ self.length = length
693
+ init
694
+ end
738
695
 
739
- mark_regarding! @source
696
+ def source=(serie)
697
+ super
698
+ @previous&.source = serie
740
699
  end
741
700
 
742
- def _restart
743
- @source.restart
701
+ attr_reader :length
702
+
703
+ def length=(value)
704
+ @length = value
705
+ @previous&.length = value
744
706
  end
745
707
 
746
- def _next_value
747
- @previous.materialize if @previous
708
+ private def _restart
709
+ @source.restart
710
+ end
748
711
 
712
+ private def _next_value
713
+ @previous&.materialize
749
714
  @previous = CutSerie.new @source, @length if @source.peek_next_value
750
715
  end
751
716
 
752
- private
753
-
754
717
  class CutSerie
755
- include Serie
718
+ include Serie.with(source: true)
756
719
 
757
720
  def initialize(serie, length)
758
- @source = serie
759
- @length = length
721
+ self.source = serie.instance
722
+ self.length = length
760
723
 
761
724
  @values = []
762
- _restart
763
-
764
- mark_as_instance!
725
+ init
765
726
  end
766
727
 
728
+ attr_accessor :length
729
+
767
730
  def _prototype!
768
731
  # TODO review why cannot get prototype of a cut serie
769
- raise PrototypingSerieError, 'Cannot get prototype of a cut serie'
732
+ raise PrototypingError, 'Cannot get prototype of a cut serie'
770
733
  end
771
734
 
772
- def _restart
735
+ private def _init
773
736
  @count = 0
774
737
  end
775
738
 
776
- def _next_value
777
- value ||= @values[@count]
739
+ private def _next_value
740
+ value = @values[@count]
778
741
  value ||= @values[@count] = @source.next_value if @count < @length
779
742
 
780
743
  @count += 1
@@ -786,30 +749,29 @@ module Musa
786
749
  (@values.size..@length - 1).each { |i| @values[i] = @source.next_value }
787
750
  end
788
751
  end
752
+
753
+ private_constant :CutSerie
789
754
  end
790
755
 
791
756
  private_constant :Cutter
792
757
 
793
758
  class Locker
794
- include Serie
795
-
796
- attr_reader :source
759
+ include Serie.with(source: true)
797
760
 
798
761
  def initialize(serie)
799
- @source = serie
762
+ self.source = serie
763
+
800
764
  @values = []
801
765
  @first_round = true
802
766
 
803
- _restart
804
-
805
- mark_regarding! @source
767
+ init
806
768
  end
807
769
 
808
- def _restart
770
+ private def _init
809
771
  @index = 0
810
772
  end
811
773
 
812
- def _next_value
774
+ private def _next_value
813
775
  if @first_round
814
776
  value = @source.next_value
815
777
 
@@ -831,34 +793,33 @@ module Musa
831
793
  private_constant :Locker
832
794
 
833
795
  class Reverser
834
- include Serie
835
-
836
- attr_reader :source
796
+ include Serie.with(source: true)
837
797
 
838
798
  def initialize(serie)
839
- @source = serie
840
- _restart false, false
841
-
842
- mark_regarding! @source
799
+ self.source = serie
800
+ init
843
801
  end
844
802
 
845
- def _instance!
803
+ def source=(serie)
804
+ raise ArgumentError, "A serie to reverse can't be infinite" if serie.infinite?
846
805
  super
847
- _restart false, true
806
+ init
848
807
  end
849
808
 
850
- def _restart(restart_sources = true, get_reversed = true)
851
- @source.restart if restart_sources
852
- @reversed = FromArray.new(next_values_array_of(@source).reverse).instance if get_reversed
809
+ private def _init
810
+ @reversed = nil
853
811
  end
854
812
 
855
- def _next_value
856
- @reversed.next_value
813
+ private def _restart
814
+ @source.restart
857
815
  end
858
816
 
859
- private
817
+ private def _next_value
818
+ @reversed ||= Constructors.S(*next_values_array_of(@source).reverse).instance
819
+ @reversed.next_value
820
+ end
860
821
 
861
- def next_values_array_of(serie)
822
+ private def next_values_array_of(serie)
862
823
  array = []
863
824
 
864
825
  until (value = serie.next_value).nil?
@@ -872,25 +833,27 @@ module Musa
872
833
  private_constant :Reverser
873
834
 
874
835
  class Randomizer
875
- include Serie
876
-
877
- attr_reader :source, :random
836
+ include Serie.with(source: true)
878
837
 
879
838
  def initialize(serie, random)
880
- @source = serie
881
- @random = random
839
+ self.source = serie
840
+ self.random = random
882
841
 
883
- _restart false
842
+ init
843
+ end
884
844
 
885
- mark_regarding! @source
845
+ attr_accessor :random
846
+
847
+ private def _init
848
+ @values = @source.to_a(duplicate: false, restart: false)
886
849
  end
887
850
 
888
- def _restart(restart_sources = true)
889
- @source.restart if restart_sources
890
- @values = @source.to_a
851
+ private def _restart
852
+ @source.restart
853
+ @values = @source.to_a(duplicate: false, restart: false)
891
854
  end
892
855
 
893
- def _next_value
856
+ private def _next_value
894
857
  if !@values.empty?
895
858
  position = @random.rand(0...@values.size)
896
859
  value = @values[position]
@@ -907,30 +870,46 @@ module Musa
907
870
  private_constant :Randomizer
908
871
 
909
872
  class Shifter
910
- include Serie
911
-
912
- attr_reader :source, :shift
873
+ include Serie.with(source: true)
913
874
 
914
875
  def initialize(serie, shift)
915
- raise ArgumentError, "cannot shift to right an infinite serie #{serie}" if shift > 0 && serie.infinite?
916
- raise ArgumentError, 'cannot shift to right: function not yet implemented' if shift > 0
917
-
918
- @source = serie
919
- @shift = shift
876
+ self.source = serie
877
+ self.shift = shift
920
878
 
921
- _restart false
879
+ init
880
+ end
922
881
 
923
- mark_regarding! @source
882
+ def source=(serie)
883
+ raise ArgumentError, "cannot shift to right an infinite serie" if @shift > 0 && serie.infinite?
884
+ super
885
+ # should _restart(false) ??? if so, we lost the shifted values of the previous serie; if not we don't shift the new serie values
886
+ # I think it's better to not _restart unless it's explicitly called by the caller
924
887
  end
925
888
 
926
- def _restart(restart_sources = true)
927
- @source.restart if restart_sources
889
+ attr_reader :shift
890
+
891
+ def shift=(value)
892
+ raise ArgumentError, "cannot shift to right an infinite serie" if value > 0 && @source&.infinite?
893
+ raise NotImplementedError, 'cannot shift to right: function not yet implemented' if value > 0
894
+
895
+ @shift = value
896
+ end
928
897
 
898
+ private def _init
929
899
  @shifted = []
930
- @shift.abs.times { @shifted << @source.next_value } if @shift < 0
900
+ @first = true
931
901
  end
932
902
 
933
- def _next_value
903
+ private def _restart
904
+ @source.restart
905
+ end
906
+
907
+ private def _next_value
908
+ if @first
909
+ @shift.abs.times { @shifted << @source.next_value } if @shift < 0
910
+ @first = false
911
+ end
912
+
934
913
  value = @source.next_value
935
914
  return value unless value.nil?
936
915
 
@@ -941,28 +920,28 @@ module Musa
941
920
  private_constant :Shifter
942
921
 
943
922
  class Remover
944
- include Serie
945
-
946
- attr_reader :source
923
+ include Serie.with(source: true, block: true)
947
924
 
948
925
  def initialize(serie, &block)
949
- @source = serie
950
- @block = block
951
- @history = []
926
+ self.source = serie
927
+ self.proc = block
952
928
 
953
- _restart false
929
+ @history = []
954
930
 
955
- mark_regarding! @source
931
+ init
956
932
  end
957
933
 
958
- def _restart(restart_sources = true)
959
- @source.restart if restart_sources
934
+ private def _init
960
935
  @history.clear
961
936
  end
962
937
 
963
- def _next_value
938
+ private def _restart
939
+ @source.restart
940
+ end
941
+
942
+ private def _next_value
964
943
  if value = @source.next_value
965
- while @block.call(value, @history)
944
+ while value && @block.call(value, @history)
966
945
  @history << value
967
946
  value = @source.next_value
968
947
  end
@@ -975,24 +954,20 @@ module Musa
975
954
  private_constant :Remover
976
955
 
977
956
  class Selector
978
- include Serie
979
-
980
- attr_reader :source
957
+ include Serie.with(source: true, block: true)
981
958
 
982
959
  def initialize(serie, &block)
983
- @source = serie
984
- @block = block
985
-
986
- _restart false
960
+ self.source = serie
961
+ self.proc = block
987
962
 
988
- mark_regarding! @source
963
+ init
989
964
  end
990
965
 
991
- def _restart(restart_sources = true)
992
- @source.restart if restart_sources
966
+ private def _restart
967
+ @source.restart
993
968
  end
994
969
 
995
- def _next_value
970
+ private def _next_value
996
971
  value = @source.next_value
997
972
  until value.nil? || @block.call(value)
998
973
  value = @source.next_value
@@ -1004,23 +979,22 @@ module Musa
1004
979
  private_constant :Selector
1005
980
 
1006
981
  class HashFromSeriesArray
1007
- include Serie
1008
-
1009
- attr_reader :source, :keys
982
+ include Serie.with(source: true)
1010
983
 
1011
984
  def initialize(serie, keys)
1012
- @source = serie
1013
- @keys = keys
1014
- _restart false
985
+ self.source = serie
986
+ self.keys = keys
1015
987
 
1016
- mark_regarding! @source
988
+ init
1017
989
  end
1018
990
 
1019
- def _restart(restart_sources = true)
1020
- @source.restart if restart_sources
991
+ attr_accessor :keys
992
+
993
+ private def _restart
994
+ @source.restart
1021
995
  end
1022
996
 
1023
- def _next_value
997
+ private def _next_value
1024
998
  array = @source.next_value
1025
999
 
1026
1000
  return nil unless array
@@ -1036,6 +1010,45 @@ module Musa
1036
1010
  end
1037
1011
 
1038
1012
  private_constant :HashFromSeriesArray
1013
+
1014
+ class LazySerieEval
1015
+ include Serie.with(source: true, block: true)
1016
+
1017
+ def initialize(serie, &block)
1018
+ self.source = serie
1019
+ self.proc = block
1020
+
1021
+ init
1022
+ end
1023
+
1024
+ def source=(serie)
1025
+ super
1026
+ @processed = nil
1027
+ end
1028
+
1029
+ def proc(&block)
1030
+ super
1031
+ @processed = nil if block
1032
+ end
1033
+
1034
+ def proc=(block)
1035
+ super
1036
+ @processed = nil if block
1037
+ end
1038
+
1039
+ private def _restart
1040
+ @processed = nil
1041
+ @source.restart
1042
+ end
1043
+
1044
+ private def _next_value
1045
+ @processed ||= @block.call(@source)
1046
+ @processed.next_value
1047
+ end
1048
+ end
1049
+
1050
+ private_constant :LazySerieEval
1039
1051
  end
1052
+
1040
1053
  end
1041
1054
  end