musa-dsl 0.22.5 → 0.23.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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