数列操作不完全指南
数据结构(Data Structure)这个词乍一听令人有些畏惧,但等你了解这些内容之后,会发现实际操作和你在excel里面能做的事情是类似的。python常用的numpy库其实就相当于一个编程版本的excel,而sonic pi也有和python类似的操作数列的指令。
一般处理数据时,最常见到的格式就是数列Array。MIDI序列可以用数列的形式存储,但sonicpi在实际读取时,数列本身长度有限,并不支持loop循环播放。所以sonic pi使用ring来存储这些序列,这样在播放序列结束之后程序又会自动回溯到开始的地方。
对于处理ring的指令,sonic pi官方文档有着重介绍,而剩下的ruby原生指令作者则一笔带过。这就导致我在看其他人代码的时候,会在一些骚操作里发现咒语一样的数列指令,其中有些是之前从未见过的。
码到用时方恨少。有些指令即便只看文档也想象不出具体应用的情形,所以只能把有可能用到的指令先罗列出来以备不时之需。下面的笔记把指令和输出结果放在一起看,对我来说看实例更有帮助。
常见操作
为了举例,在这里新建一个chain
chain = (ring 0,1,2,3,4,5,6,7,8)
之后每步操作都是接续上一个步骤来的
Reverse
倒序
chain=chain.reverse
puts chain, 'reverse'
#(ring 8, 7, 6, 5, 4, 3, 2, 1, 0) "reverse"
Sort
按数值由小到大排列
chain=chain.sort
puts chain, 'sort'
#(ring 0, 1, 2, 3, 4, 5, 6, 7, 8) "sort"
Shuffle
随机乱序
似乎可以加random seed,每次生成相同的数列
chain = chain.shuffle
puts chain, 'shuffle'
#(ring 1, 4, 2, 8, 6, 5, 0, 3, 7) "shuffle"
Pick/Choose
Pick/Choose都是随机挑选一个数值
但是注意pick的输出仍是ring
而choose的输出是一个数值
a=chain.pick
b=chain.choose
puts 'pick',a,'choose',b
#"pick" (ring 0) "choose" 6
pick也可以随机选出多个数值
pickChain = chain.pick(3)
puts 'pick 3 times', pickChain
#"pick 3 times" (ring 2, 1, 6)
Take
选出你指定的前N个数
puts 'original chain',chain
take5=chain.take(5)
puts 'take 5', take5
#"original chain" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7)
#"take 5" (ring 1, 4, 2, 8, 6)
Drop
删除数列前N个数
puts 'original chain',chain
drop3=chain.drop(3)
puts 'drop 3', drop3
#"original chain" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7)
#"drop 3" (ring 8, 6, 5, 0, 3, 7)
Butlast
删除数列最后一个数
puts 'original chain',chain
butlast=chain.butlast
puts 'butlast', butlast
#"original chain" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7)
#"butlast" (ring 1, 4, 2, 8, 6, 5, 0, 3)
Drop_last
选出数列最后N个数
puts 'original chain',chain
drop_last3=chain.drop_last(3)
puts 'drop_last3', drop_last3
#"original chain" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7)
#"drop_last3" (ring 1, 4, 2, 8, 6, 5)
Repeat
原有数列重复N遍
例如(0,1)重复3遍->(0,1,0,1,0,1)
repeat=chain.repeat(3)
Mirror
数列对称排列,原数列最后一个数(7)重复出现两次
puts 'original chain',chain
mirror=chain.mirror
puts 'mirror', mirror
#"original chain" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7)
#"mirror" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7, 7,3,0,5,6,8,2,4,1)
Reflect
数列对称排列,原数列最后一个数(7)不重复出现
reflect=chain.reflect
puts 'reflect', reflect
#"original chain" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7)
#"reflect" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7,3,0,5,6,8,2,4,1)
Scale
数列内数值做乘法
puts 'original chain',chain
scale2=chain.scale(2)
puts 'scale2', scale2
#"original chain" (ring 1, 4, 2, 8, 6, 5, 0, 3, 7)
# scale2 (ring 2.0, 8.0, 4.0, 16.0, 12.0, 10.0, 0.0, 6.0, 14.0)
多个“点方法”可以写在一起
puts 'original chain',chain
newChain=chain.scale(2).sort.take(4)
puts 'newChain', newChain
#"newChain" (ring 0.0,2.0,4.0,6.0)
遍历数列内数值
还是先写一个ring
chain = (ring 60,70,80,90)
方法一
live_loop :loop1 do
chain.each do |num|
play num
sleep 1
end
end
符合一般脚本写法的遍历格式
Chain.each在进行遍历操作
将遍历的每个元素用num替代
之后写上对每个元素的具体操作过程
方法一更适合写在function里
当你对ring内各元素做具体操作时可能会用到
方法二
与方法一相同的操作
在读取MIDI序列时
sonic pi比较推荐下面这种遍历写法
这里使用tick最方便
live_loop :loop2 do
play chain.tick
sleep 1
end