How to change one element in a ring


#1

Hi,
How to change a given element in a ring? Example:
notes = (scale :e3, :minor_pentatonic)
I would like to change say the third element for the note :r.
notes[2] = :r #generates an error

Answer or link to docs page would be very appreciated!
Patrick


#2

Rings are immutable, so the solution is to build a fresh string.
With your example:

notes = scale :e3,:minor_pentatonic
puts notes # ===> (ring 52, 55, 57, 59, 62, 64)
notes2 = notes[0..1]+[:r]+notes[3..-1]
notes=notes2
puts notes #==> (ring 52, 55, :r, :r, 62, 64)

A function which can do this for you is shown below.
NB the position to replace is numbered from 0 upwards (0 gives is first value in the ring)

notes = scale :e3,:minor_pentatonic

puts notes # ==> (ring 52, 55, 57, 59, 62, 64)

define :update do |n,position,val|
  pos =[0, [position, n.length - 1].min].max #set limits to position
  #puts pos #for debugging
  l=pos-1;r=pos+1 #calc slice positions
  #puts "l,r",l,r #for debugging
  if l < 0 #special case. Insert val at start of ring
    return [val]+n[1..-1]
  elsif
    r > n.length - 1 #special case insert val at end of ring
    return n[0..-2]+[val]
  else
    return n[0..l]+[val]+n[r..-1] #normal cases
  end
end

notes = update(notes,3,:r)

puts notes # ==> (ring 52, 55, 57, :r, 62, 64)

In this example the FOURTH note is replaced counting from 0 this is 0,1,2,3
If you want you can adjust things to count from 1 by inserting line

position -= 1

as the first line in the definition

EDIT the min max methods are used to clamp position so that it is not outside the ring. Otherwise
if you put something like update(notes,400,:r) it would give an error. With the error checking this will replace the last character in the ring.

EDIT 2
you may be able to write this a bit more nicely by using the .take and .take_last methods listed in the Sonic Pi tutorial section shown below

http://sonic-pi.net/tutorial.html#section-8-5


#3

Thank you @robin.newman!
Regards,
Patrick


#4

I’ve now answered my last edit. Here is a simpler version using the methods in section 8.5 of the tutorial

define :update do |n,position,val|
  puts position #for debugging
  if position < 1 #special case. Insert val at start of ring
    return [val]+n.drop(1)
  elsif
    position >= n.length #special case insert val at end of ring
    return n.drop_last(1)+[val]
  else
    return n.take(position-1)+[val]+n.take_last(n.length-position) #normal cases
  end
end

notes = scale :e3,:minor_pentatonic
puts notes # ===> (ring 52, 55, 57, 59, 62, 64)

puts update( notes,2,:r) # => (ring 52, :r, 57, 59, 62, 64)