Module: Xi::Pattern::Transforms

Included in:
Xi::Pattern
Defined in:
lib/xi/pattern/transforms.rb

Instance Method Summary collapse

Instance Method Details

#%(numeric) ⇒ Pattern

Performs a scalar modulo against numeric

For each value from pattern, return modulo of value divided by numeric. Values from pattern that do not respond to #% are ignored.

Examples:

peek (1..5).p % 2                     #=> [1, 0, 1, 0, 1]
peek [0, 1, 2, :bar, 4, 5, 6].p % 3   #=> [0, 1, 2, :bar, l, 2, 0]

Parameters:

  • numeric (Numeric)

Returns:



106
107
108
# File 'lib/xi/pattern/transforms.rb', line 106

def %(numeric)
  map { |v| v.respond_to?(:%) ? v % numeric : v }
end

#*(numeric) ⇒ Pattern

Performs a scalar multiplication with numeric

For each value from pattern, multiplicate with numeric. Values that do not respond to #* are ignored.

Examples:

peek [1, 2, 4].p * 2      #=> [2, 4, 8]
peek [1, :foo].p * 2      #=> [2, :foo]

Parameters:

  • numeric (Numeric)

Returns:



74
75
76
# File 'lib/xi/pattern/transforms.rb', line 74

def *(numeric)
  map { |v| v.respond_to?(:*) ? v * numeric : v }
end

#**(numeric) ⇒ Pattern Also known as: ^

Raises each value to the power of numeric, which may be negative or fractional.

Values from pattern that do not respond to #** are ignored.

Examples:

peek (0..5).p ** 2        #=> [0, 1, 4, 9, 16, 25]
peek [1, 2, 3].p ** -2    #=> [1, (1/4), (1/9)]

Parameters:

  • numeric (Numeric)

Returns:



122
123
124
# File 'lib/xi/pattern/transforms.rb', line 122

def **(numeric)
  map { |v| v.respond_to?(:**) ? v ** numeric : v }
end

#+(object) ⇒ Pattern

Concatenate object pattern or perform a scalar sum with object

If object is a Pattern, concatenate the two patterns. Else, for each value from pattern, sum with object. Values that do not respond to #+ are ignored.

Examples:

Concatenation of patterns

peek [1, 2, 3].p + [4, 5, 6].p    #=> [1, 2, 3, 4, 5, 6]

Scalar sum

peek [1, 2, 3].p + 60             #=> [61, 62, 63]
peek [0.25, 0.5].p + 0.125        #=> [0.375, 0.625]
peek [0, :foo, 2].p + 1           #=> [1, :foo, 3]

Parameters:

  • object (Pattern, Numeric)

    pattern or numeric

Returns:



35
36
37
38
39
40
41
42
43
44
# File 'lib/xi/pattern/transforms.rb', line 35

def +(object)
  if object.is_a?(Pattern)
    Pattern.new(self, size: size + object.size) { |y, d|
      each { |v| y << v }
      object.each { |v| y << v }
    }
  else
    map { |v| v.respond_to?(:+) ? v + object : v }
  end
end

#-(numeric) ⇒ Pattern

Performs a scalar substraction with numeric

For each value from pattern, substract with numeric. Values that do not respond to #- are ignored.

Examples:

peek [1, 2, 3].p - 10       #=> [-9, -8, -7]
peek [1, :foo, 3].p - 10    #=> [-9, :foo, -7]

Parameters:

  • numeric (Numeric)

Returns:



58
59
60
# File 'lib/xi/pattern/transforms.rb', line 58

def -(numeric)
  map { |v| v.respond_to?(:-) ? v - numeric : v }
end

#-@Pattern

Negates every number in the pattern

Non-numeric values are ignored.

Examples:

peek -[10, 20, 30].p    #=> [-10, -20, -30]
peek -[1, -2, 3].p      #=> [-1, 2, -3]

Returns:



14
15
16
# File 'lib/xi/pattern/transforms.rb', line 14

def -@
  map { |v| v.respond_to?(:-@) ? -v : v }
end

#/(numeric) ⇒ Pattern

Performs a scalar division by numeric

For each value from pattern, divide by numeric. Values that do not respond to #/ are ignored.

Examples:

peek [1, 2, 4].p / 2      #=> [(1/2), (1/1), (2/1)]
peek [0.5, :foo].p / 2    #=> [0.25, :foo]

Parameters:

  • numeric (Numeric)

Returns:



90
91
92
# File 'lib/xi/pattern/transforms.rb', line 90

def /(numeric)
  map { |v| v.respond_to?(:/) ? v / numeric : v }
end

#bounce(skip_extremes = true) ⇒ Pattern

Traverses the pattern in order and then in reverse order, skipping first and last values if skip_extremes is true.

Examples:

peek (0..3).p.bounce   #=> [0, 1, 2, 3, 2, 1]
peek 10.p.bounce       #=> [10]

with skip_extremes=false

peek (0..3).p.bounce(false)   #=> [0, 1, 2, 3, 3, 2, 1, 0]

Parameters:

  • skip_extremes (Boolean) (defaults to: true)

    Skip first and last values to avoid repeated values (default: true)

Returns:



192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/xi/pattern/transforms.rb', line 192

def bounce(skip_extremes=true)
  return self if size == 0 || size == 1

  new_size = skip_extremes ? size * 2 - 2 : size * 2
  Pattern.new(self, size: new_size) { |y|
    each { |v| y << v }
    last_i = size - 1
    reverse_each.with_index { |v, i|
      y << v unless skip_extremes && (i == 0 || i == last_i)
    }
  }
end

#denormalize(min, max) ⇒ Pattern Also known as: denorm

Scales a pattern of normalized values (0..1) to a custom range min..max

This is inverse of #normalize Values from pattern that do not respond to #* are ignored.

Examples:

peek [0.01, 0.02, 0.03, 0.04, 0.05].p.denormalize(0, 100)
  #=> [1.0, 2.0, 3.0, 4.0, 5.0]
peek [0, 0.25, 0.50, 0.75].p.denormalize(0, 0x100)
  #=> [0, 64.0, 128.0, 192.0]

Parameters:

  • min (Numeric)
  • max (Numeric)

Returns:



240
241
242
# File 'lib/xi/pattern/transforms.rb', line 240

def denormalize(min, max)
  map { |v| v.respond_to?(:*) ? (max - min) * v + min : v }
end

#every(n) {|Pattern| ... } ⇒ Pattern

Splices a new pattern returned from block every n cycles

Parameters:

  • n (Numeric)

Yields:

Yield Returns:

  • (Pattern)

    transformed subpattern

Returns:

See Also:



447
448
449
450
451
452
453
# File 'lib/xi/pattern/transforms.rb', line 447

def every(n, &block)
  fn = proc { |_, s, _|
    m = (s + 1) % n
    m >= 0 && m < 1
  }
  self.when(fn, &block)
end

#every_iter(n) {|Pattern| ... } ⇒ Pattern

Splices a new pattern returned from block every n iterations

Parameters:

  • n (Numeric)

Yields:

Yield Returns:

  • (Pattern)

    transformed subpattern

Returns:

See Also:



464
465
466
467
468
469
470
# File 'lib/xi/pattern/transforms.rb', line 464

def every_iter(n, &block)
  fn = proc { |_, _, _, i|
    m = (i + 1) % n
    m >= 0 && m < 1
  }
  self.when(fn, &block)
end

#fast(num) ⇒ Pattern

Advance a pattern by shrinking start and duration of events num times.

It is the inverse operation of #slow

Examples:

peek_events %w(a b c d).p([1/2, 1/4]).fast(2)
  #=> [E["a",0,1/4], E["b",1/4,1/8], E["c",3/8,1/4], E["d",5/8,1/8]]

Parameters:

  • num (Numeric)

Returns:

See Also:



293
294
295
# File 'lib/xi/pattern/transforms.rb', line 293

def fast(num)
  Pattern.new(self, delta: delta.p / num)
end

#normalize(min, max) ⇒ Pattern Also known as: norm

Normalizes a pattern of values that range from min to max to 0..1

Values from pattern that do not respond to #- are ignored.

Examples:

peek (1..5).p.normalize(0, 100)
  #=> [(1/100), (1/50), (3/100), (1/25), (1/20)]
peek [0, 0x40, 0x80, 0xc0].p.normalize(0, 0x100)
  #=> [(0/1), (1/4), (1/2), (3/4)]

Parameters:

  • min (Numeric)
  • max (Numeric)

Returns:



219
220
221
# File 'lib/xi/pattern/transforms.rb', line 219

def normalize(min, max)
  map { |v| v.respond_to?(:-) ? (v - min) / (max - min) : v }
end

#rand(repeats = 1) ⇒ Pattern

Choose items from the list randomly, repeats number of times

Examples:

peek [1, 2, 3].p.rand             #=> [2]
peek [1, 2, 3, 4].p.rand(6)       #=> [1, 3, 2, 2, 4, 3]

Parameters:

  • repeats (Integer, Symbol) (defaults to: 1)

    number or inf (default: 1)

Returns:

See Also:

  • Pattern::Generators::ClassMethods#rand


370
371
372
# File 'lib/xi/pattern/transforms.rb', line 370

def rand(repeats=1)
  P.rand(self, repeats)
end

#repeat_each(times) ⇒ Pattern

Repeats each value times

times can also be an enumerable or a finite Pattern. In this case, for each value in times, it will yield each value of original pattern repeated a number of times based on that times value.

Examples:

peek [1, 2, 3].p.repeat_each(2)   #=> [1, 1, 2, 2, 3, 3]
peek [1, 2, 3].p.repeat_each(3)   #=> [1, 1, 1, 2, 2, 2, 3, 3, 3]
peek [1, 2, 3].p.repeat_each([3,2]), 15
  #=> [1, 1, 1, 2, 2, 2, 3, 3, 3, 1, 1, 2, 2, 3, 3]

Parameters:

  • times (Numeric, #each)

Returns:



345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/xi/pattern/transforms.rb', line 345

def repeat_each(times)
  times_pat = times.p

  if times_pat.infinite?
    fail ArgumentError, 'times must be finite'
  end

  Pattern.new(self, size: size * times_pat.size) do |y|
    times_pat.each do |t|
      each { |v| t.times { y << v } }
    end
  end
end

#scale(min_from, max_from, min_to, max_to) ⇒ Pattern

Scale from one range of values to another range of values

Examples:

peek [0,2,4,1,3,6].p.scale(0, 6, 0, 0x7f)
  #=> [(0/1), (127/3), (254/3), (127/6), (127/2), (127/1)]

Parameters:

  • min_from (Numeric)
  • max_from (Numeric)
  • min_to (Numeric)
  • max_to (Numeric)

Returns:



257
258
259
# File 'lib/xi/pattern/transforms.rb', line 257

def scale(min_from, max_from, min_to, max_to)
  normalize(min_from, max_from).denormalize(min_to, max_to)
end

#seq(repeats = 1, offset = 0) ⇒ Pattern

Cycles pattern repeats number of times, shifted by offset

Examples:

peek [1, 2, 3].p.seq              #=> [1, 2, 3]
peek [1, 2, 3].p.seq(2)           #=> [1, 2, 3, 1, 2, 3]
peek [1, 2, 3].p.seq(1, 1)        #=> [2, 3, 1]
peek [1, 2, 3].p.seq(2, 2)        #=> [3, 2, 1, 3, 2, 1]

Parameters:

  • repeats (Integer) (defaults to: 1)

    number (defaut: 1)

  • offset (Integer) (defaults to: 0)

    (default: 0)

Returns:



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/xi/pattern/transforms.rb', line 139

def seq(repeats=1, offset=0)
  unless repeats.is_a?(Integer) && repeats >= 0
    fail ArgumentError, "repeats must be a non-negative Integer"
  end

  unless offset.is_a?(Integer) && offset >= 0
    fail ArgumentError, "offset must be a non-negative Integer"
  end

  Pattern.new(self, size: size * repeats) do |y|
    rep = repeats

    loop do
      if rep != inf
        rep -= 1
        break if rep < 0
      end

      c = offset
      offset_items = []

      is_empty = true
      each do |v|
        is_empty = false
        if c > 0
          offset_items << v
          c -= 1
        else
          y << v
        end
      end

      offset_items.each { |v| y << v }

      break if is_empty
    end
  end
end

#shuf(repeats = 1) ⇒ Pattern

Shuffle the list in random order, and use the same random order repeats times

Examples:

peek [1, 2, 3, 4, 5].p.xrand    #=> [4]
peek [1, 2, 3].p.xrand(8)       #=> [1, 3, 2, 3, 1, 2, 3, 2]

Parameters:

  • repeats (Integer, Symbol) (defaults to: 1)

    number or inf (default: 1)

Returns:

See Also:

  • Pattern::Generators::ClassMethods#shuf


402
403
404
# File 'lib/xi/pattern/transforms.rb', line 402

def shuf(repeats=1)
  P.shuf(self, repeats)
end

#slow(num) ⇒ Pattern

Slows down a pattern by stretching start and duration of events num times.

It is the inverse operation of #fast

Examples:

peek_events %w(a b c d).p([1/4, 1/8, 1/6]).slow(2)
  #=> [E["a",0,1/2], E["b",1/2,1/4], E["c",3/4,1/3], E["d",13/12,1/2]]

Parameters:

  • num (Numeric)

Returns:

See Also:



275
276
277
# File 'lib/xi/pattern/transforms.rb', line 275

def slow(num)
  Pattern.new(self, delta: delta.p * num)
end

#sometimes(probability = 0.5) ⇒ Pattern

Based on probability, it yields original value or nil

probability can also be an enumerable or a finite Pattern. In this case, for each value in probability it will enumerate original pattern based on that probability value.

Examples:

peek (1..6).p.sometimes        #=> [1, nil, 3, nil, 5, 6]
peek (1..6).p.sometimes(1/4)   #=> [nil, nil, nil, 4, nil, 6]
peek (1..6).p.sometimes([0.5, 1]), 12
  #=> [1, 2, nil, nil, 5, 6, 1, 2, 3, 4, 5, 6]

Parameters:

  • probability (Numeric, #each) (defaults to: 0.5)

    (default=0.5)

Returns:



314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/xi/pattern/transforms.rb', line 314

def sometimes(probability=0.5)
  prob_pat = probability.p

  if prob_pat.infinite?
    fail ArgumentError, 'times must be finite'
  end

  Pattern.new(self, size: size * prob_pat.size) do |y|
    prob_pat.each do |prob|
      each { |v| y << (Kernel.rand < prob ? v : nil) }
    end
  end
end

#when(test_proc) {|Pattern| ... } ⇒ Pattern, Enumerator

Returns a new Pattern where values for which test_proc are true are yielded as a pattern to another block

If no block is given, an Enumerator is returned.

These values are grouped together as a “subpattern”, then yielded to block for further transformation and finally spliced into the original pattern. test_proc will be called with value, start and duration as parameters.

Parameters:

  • test_proc (#call)

Yields:

Yield Returns:

  • (Pattern)

    transformed subpattern

Returns:



421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/xi/pattern/transforms.rb', line 421

def when(test_proc, &block)
  return enum_for(__method__, test_proc) if block.nil?

  Pattern.new(self) do |y|
    each_event do |v, s, d, i|
      if test_proc.call(v, s, d, i)
        new_pat = block.call(self)
        new_pat.each_event(s)
          .take_while { |_, s_, d_| s_ + d_ <= s + d }
          .each { |v_, _| y << v_ }
      else
        y << v
      end
    end
  end
end

#xrand(repeats = 1) ⇒ Pattern

Choose randomly, but only allow repeating the same item after yielding all items from the list.

Examples:

peek [1, 2, 3, 4, 5].p.xrand    #=> [4]
peek [1, 2, 3].p.xrand(8)       #=> [1, 3, 2, 3, 1, 2, 3, 2]

Parameters:

  • repeats (Integer, Symbol) (defaults to: 1)

    number or inf (default: 1)

Returns:

See Also:

  • Pattern::Generators::ClassMethods#xrand


386
387
388
# File 'lib/xi/pattern/transforms.rb', line 386

def xrand(repeats=1)
  P.xrand(self, repeats)
end