如何解决在Ruby中调用函数的问题?

问题描述 投票:0回答:2

我正在尝试使用在Github中找到的一些红宝石代码。我已经下载了代码,并进行了必要的“ requires”导入,并尝试运行它,如github存储库中的自述文件中所述。代码如下:

在文件pcset_test.rb中,代码如下:

require './pcset.rb'
require 'test/unit'

#
# When possible, test cases are adapted from 
# Introduction to Post-Tonal Theory by Joseph N. Straus,
# unless obvious or otherwise noted.
#

class PCSetTest < Test::Unit::TestCase
  def test_init
    #assert_raise(ArgumentError) {PCSet.new []}
    assert_raise(ArgumentError) {PCSet.new [1, 2, 3, 'string']}
    assert_raise(ArgumentError) {PCSet.new "string"}
    assert_raise(ArgumentError) {PCSet.new [1, 2, 3.6, 4]}
    assert_equal([0, 1, 2, 9], PCSet.new([0, 1, 2, 33, 13]).pitches)
    assert_equal([3, 2, 1, 11, 10, 0], PCSet.new_from_string('321bac').pitches)
    assert_equal([0,2,4,5,7,11,9], PCSet.new([12,2,4,5,7,11,9]).pitches)
    assert_nothing_raised() {PCSet.new []}
  end

  def test_inversion

  end

  def test_transposition

  end

  def test_multiplication

  end

  #
  # set                   normal                 prime                 forte #
  # 0,2,4,7,8,11          7,8,11,0,2,4           0,1,4,5,7,9           6-31
  # 0,1,2,4,5,7,11        11,0,1,2,4,5,7         0,1,2,3,5,6,8         7-Z36
  # 0,1,3,5,6,7,9,10,11   5,6,7,9,10,11,0,1,3    0,1,2,3,4,6,7,8,10    9-8
  #

  def test_normal_form
    testPC = PCSet.new [0,4,8,9,11]
    assert_kind_of(PCSet, testPC.normal_form)
    assert_equal([8,9,11,0,4], testPC.normal_form.pitches)
    assert_equal([10,1,4,6], PCSet.new([1,6,4,10]).normal_form.pitches)
    assert_equal([2,4,8,10], PCSet.new([10,8,4,2]).normal_form.pitches)
    assert_equal([7,8,11,0,2,4], PCSet.new([0,2,4,7,8,11]).normal_form.pitches)
    assert_equal([11,0,1,2,4,5,7], PCSet.new([0,1,2,4,5,7,11]).normal_form.pitches)
    assert_equal([5,6,7,9,10,11,0,1,3], PCSet.new([0,1,3,5,6,7,9,10,11]).normal_form.pitches)  
  end

  def test_prime_form
    assert_equal([0,1,2,6], PCSet.new([5,6,1,7]).prime.pitches)
    assert_equal([0,1,4], PCSet.new([2,5,6]).prime.pitches)
    assert_equal([0,1,4,5,7,9], PCSet.new([0,2,4,7,8,11]).prime.pitches)
    assert_equal([0,1,2,3,5,6,8], PCSet.new([0,1,2,4,5,7,11]).prime.pitches)
    assert_equal([0,1,2,3,4,6,7,8,10], PCSet.new([0,1,3,5,6,7,9,10,11]).prime.pitches)
  end

  def test_set_class
    testPcs = PCSet.new([2,5,6])
    testPrime = testPcs.prime
    assert_equal([
        [2,5,6], [3,6,7], [4,7,8], [5,8,9], [6,9,10], [7,10,11],
        [8,11,0],[9,0,1], [10,1,2],[11,2,3],[0,3,4],  [1,4,5],
        [6,7,10],[7,8,11],[8,9,0], [9,10,1],[10,11,2],[11,0,3],
        [0,1,4], [1,2,5], [2,3,6], [3,4,7], [4,5,8],  [5,6,9]
      ].sort, PCSet.new([2,5,6]).set_class.map{|x| x.pitches})
    assert_equal(testPcs.set_class.map{|x| x.pitches}, testPrime.set_class.map{|x| x.pitches})
  end

  def test_interval_vector
    assert_equal([2,1,2,1,0,0], PCSet.new([0,1,3,4]).interval_vector)
    assert_equal([2,5,4,3,6,1], PCSet.new([0,1,3,5,6,8,10]).interval_vector)
    assert_equal([0,6,0,6,0,3], PCSet.new([0,2,4,6,8,10]).interval_vector)
  end

  def test_complement
    assert_equal([6,7,8,9,10,11], PCSet.new([0,1,2,3,4,5]).complement.pitches)
    assert_equal([3,4,5], PCSet.new([0,1,2], 6).complement.pitches)
  end

  #
  # Test values from (Morris 1991), pages 105-111
  # Citation:
  # Morris. Class Notes for Atonal Music Theory
  # Lebanon, NH. Frog Peak Music, 1991.
  #
  def test_invariance_vector
    assert_equal([1,0,0,0,5,6,5,5],PCSet.new([0,2,5]).invariance_vector)
    assert_equal([2,2,2,2,6,6,6,6],PCSet.new([0,1,6,7]).invariance_vector)
    assert_equal([6,6,6,6,6,6,6,6],PCSet.new([0,2,4,6,8,10]).invariance_vector)
    assert_equal([1,0,0,0,0,0,0,0],PCSet.new([0,1,2,3,4,5,8]).invariance_vector)
    assert_equal([1,0,0,1,0,0,0,0],PCSet.new([0,1,2,3,5,6,8]).invariance_vector)
    assert_equal([12,12,12,12,0,0,0,0],PCSet.new([0,1,2,3,4,5,6,7,8,9,10,11]).invariance_vector)
  end

  #
  # Test values from (Huron 1994). Huron rounds, thus the 0.01 margin of error.
  # Citation:
  # Huron. Interval-Class Content in Equally Tempered Pitch-Class Sets: 
  # Common Scales Exhibit Optimum Tonal Consonance. 
  # Music Perception (1994) vol. 11 (3) pp. 289-305
  #
  def test_huron
    h1 = PCSet.new([0,1,2,3,4,5,6,7,8,9,10,11]).huron
    assert_in_delta(-0.2, h1[0], 0.01)
    assert_in_delta(0.21, h1[1], 0.01)
    h2 = PCSet.new([0,2,4,5,7,9,11]).huron
    assert_in_delta(4.76, h2[0], 0.01)
    assert_in_delta(0.62, h2[1], 0.01)
  end

  def test_coherence

  end
end

并且在文件pcset.rb中的以下代码:

#
# => PCSet Class for Ruby
# => Beau Sievers
# => Hanover, Fall 2008.
#
#
# TODO: Make this a module to avoid namespace collisions.
#       Lilypond and MusicXML output
#

include Math

def choose(n, k)
  return [[]] if n.nil? || n.empty? && k == 0
  return [] if n.nil? || n.empty? && k > 0
  return [[]] if n.size > 0 && k == 0
  c2 = n.clone
  c2.pop
  new_element = n.clone.pop
  choose(c2, k) + append_all(choose(c2, k-1), new_element)
end

def append_all(lists, element)
  lists.map { |l| l << element }
end

def array_to_binary(array)
  array.inject(0) {|sum, n| sum + 2**n}
end

# the following method is horrifically inelegant 
# but avoids monkey-patching.
# TODO: do this right, incl. error checking
def pearsons(x, y)
  if !x.is_a?(Array) || !y.is_a?(Array) then raise StandardError, "x and y must be arrays", caller end
  if x.size != y.size then raise StandardError, "x and y must be same size", caller end
  sum_x = x.inject(0) {|sum, n| sum + n}
  sum_y = y.inject(0) {|sum, n| sum + n}
  sum_square_x = x.inject(0) {|sum, n| sum + n * n}
  sum_square_y = y.inject(0) {|sum, n| sum + n * n}
  xy = []
  x.zip(y) {|a, b| xy.push(a * b)}
  sum_xy = xy.inject(0) {|sum, n| sum + n}
  num = sum_xy - ((sum_x * sum_y)/x.size)
  den = Math.sqrt((sum_square_x - ((sum_x*sum_x)/x.size)) * (sum_square_y - ((sum_y*sum_y)/x.size)))
  (num/den)
end

class PCSet
  include Comparable
  attr_reader :pitches, :base, :input

  def initialize(pcarray, base = 12)
    if pcarray.instance_of?(Array) && pcarray.all?{|pc| pc.instance_of?(Fixnum)}
      @base, @input = base, pcarray
      @pitches = pcarray.map{ |x| x % @base }.uniq
    else
      raise ArgumentError, "Improperly formatted PC array", caller
    end
  end

  def PCSet.new_from_string(pcstring, base = 12)
    if base > 36 then raise StandardError, "Use PCSet.new to create pcsets with a base larger than 36", caller end
    pcarray = []
    pcstring.downcase.split(//).each do |c|
      if c <= 'z' and c >= '0' then pcarray.push(c.to_i(36)) end
    end
    PCSet.new pcarray, base
  end

  def <=>(pcs)
    @pitches <=> pcs.pitches
  end

  def [](index)
    @pitches[index]
  end

  # Intersection
  def &(other)
    PCSet.new @pitches & other.pitches
  end

  # Union
  def |(other)
    PCSet.new @pitches | other.pitches
  end

  def inspect
    @pitches.inspect
  end

  def length  
    @pitches.length
  end

  def invert(axis = 0)
    PCSet.new @pitches.map {|x| (axis-x) % @base}
  end

  def invert!(axis = 0)
    @pitches.map! {|x| (axis-x) % @base}
  end

  def transpose(interval)
    PCSet.new @pitches.map {|x| (x + interval) % @base}
  end

  def transpose!(interval)
    @pitches.map! {|x| (x + interval) % @base}
  end

  def multiply(m = 5)
    PCSet.new @pitches.map {|x| (x * m) % @base}
  end

  def multiply!(m = 5)
    @pitches.map! {|x| (x * m) % @base}
  end

  def zero
    transpose(-1 * @pitches[0])
  end

  def zero!
    transpose!(-1 * @pitches[0])
  end

  def transpositions
    (0..(@base-1)).to_a.map{|x| @pitches.map {|y| (y + x) % @base}}.sort.map {|x| PCSet.new x}
  end

  def transpositions_and_inversions(axis = 0)
    transpositions + invert(axis).transpositions
  end

  #
  # Normal form after Straus. Morris and AthenaCL do this differently.
  #
  def normal_form
    tempar = @pitches.sort
    arar = []                 # [[1,4,7,8,10],[4,7,8,10,1], etc.] get each cyclic variation
    tempar.each {arar.push PCSet.new(tempar.unshift(tempar.pop))}
    most_left_compact(arar)
  end

  def normal_form!
    @pitches = normal_form.pitches
  end

  def is_normal_form?
    self.pitches == self.normal_form.pitches
  end

  def set_class
    transpositions_and_inversions.map{|pcs| pcs.normal_form}.sort
  end

  def prime
    most_left_compact([normal_form.zero, invert.normal_form.zero])
  end

  def prime!
    self.pitches = self.prime.pitches
  end

  def is_prime?
    self.pitches == self.prime.pitches
  end

  def complement
    new_pitches = []
    @base.times do |p|
      if [email protected]? p then
        new_pitches.push p
      end
    end
    PCSet.new new_pitches
  end

  def full_interval_vector
    pairs = choose(@pitches, 2)                       # choose every pc pair
    intervals = pairs.map {|x| (x[1] - x[0]) % @base} # calculate every interval
    i_vector = Array.new(@base-1).fill(0)
    intervals.each {|x| i_vector[x-1] += 1}            # count the intervals
    i_vector
  end

  def interval_vector
    i_vector = full_interval_vector
    (0..((@base-1)/2)-1).each {|x| i_vector[x] += i_vector.pop}
    i_vector
  end

  #
  # Morris's invariance vector
  #
  def invariance_vector(m = 5)
    t = transpositions.map!{|pcs| self & pcs}
    ti = invert.transpositions.map!{|pcs| self & pcs}    
    tm = multiply(m).transpositions.map!{|pcs| self & pcs}   
    tmi = invert.multiply(m).transpositions.map!{|pcs| self & pcs}   
    tc = complement.transpositions.map!{|pcs| self & pcs}   
    tic = complement.invert.transpositions.map!{|pcs| self & pcs}
    tmc = complement.multiply(m).transpositions.map!{|pcs| self & pcs}
    tmic = complement.invert.multiply(m).transpositions.map!{|pcs| self & pcs}

    [t, ti, tm, tmi, tc, tic, tmc, tmic].map{|x| x.reject{|pcs| pcs.pitches != @pitches}.length}
  end

  # Huron's aggregate dyadic consonance measure. 
  # Huron. Interval-Class Content in Equally Tempered Pitch-Class Sets: 
  # Common Scales Exhibit Optimum Tonal Consonance. 
  # Music Perception (1994) vol. 11 (3) pp. 289-305
  def huron
    if @base != 12 then raise StandardError, "PCSet.huron only makes sense for mod 12 pcsets", caller end

    #              m2/M7   M2/m7  m3/M6  M3/m6  P4/P5   A4/d5
    huron_table = [-1.428, -0.582, 0.594, 0.386, 1.240, -0.453]
    interval_consonance = []
    interval_vector.zip(huron_table) {|x, y| interval_consonance.push(x * y) }
    aggregate_dyadic_consonance = interval_consonance.inject {|sum, n| sum + n}
    [aggregate_dyadic_consonance, pearsons(interval_vector, huron_table)]
  end

  #
  # Balzano's vector of relations. Citation for all Balzano methods:
  # 
  # Balzano. "The Pitch Set as a Level of Description for Studying Musical
  # Pitch Perception" in Music, Mind, and Brain ed. Clynes. Plenum Press. 1982.
  #
  def vector_of_relations
    (0..length-1).to_a.map do |i|
      (0..length-1).to_a.map do |j|
        (@pitches[(i + j) % length] - @pitches[i]) % @base
      end
    end
  end

  #
  # Checks if the set satisfies Balzano's uniqueness.
  #
  def is_unique?
    vector_of_relations.uniq.size == vector_of_relations.size
  end

  #
  # Checks if the set satisfies Balzano's scalestep-semitone coherence.
  # For all s[i] and s[i1]:
  # j < k => v[i][j] < v[i1][k]
  # Where j and k are scalestep-counting indices.
  # And unless v[i][j] == 6 (a tritone), in which case the strict inequality is relaxed.
  #
  def is_coherent?
    v = vector_of_relations
    truth_array = []
    all_pair_indices = choose((0..length-1).to_a, 2)
    all_pair_indices.each do |i, i1|
      all_pair_indices.each do |j, k|
        if v[i][j] == 6
          truth_array.push(v[i][j] <= v[i1][k])
        else
          truth_array.push(v[i][j] < v[i1][k])    
        end
        if v[i1][j] == 6
          truth_array.push(v[i1][j] <= v[i][k])
        else
          truth_array.push(v[i1][j] < v[i][k])
        end
      end
    end
    !truth_array.include?(false)
  end

  #
  # Strict Balzano coherence, no inequality relaxation for tritones.
  #
  def is_strictly_coherent?
    v = vector_of_relations
    truth_array = []
    all_pair_indices = choose((0..length-1).to_a, 2)
    all_pair_indices.each do |i, i1|
      all_pair_indices.each do |j, k|
        truth_array.push(v[i][j] < v[i1][k])
        truth_array.push(v[i1][j] < v[i][k])
      end
    end
    !truth_array.include?(false)
  end

  def notes(middle_c = 0)
    noteArray = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']
    if @base != 12 then raise StandardError, "PCSet.notes only makes sense for mod 12 pcsets", caller end
    out_string = String.new
    transpose(-middle_c).pitches.each do |p|
      out_string += noteArray[p] + ", "
    end
    out_string.chop.chop
  end

  def info
    print "modulo: #{@base}\n"
    print "raw input: #{@input.inspect}\n"
    print "pitch set: #{@pitches.inspect}\n"
    print "notes: #{notes}\n"
    print "normal: #{normal_form.inspect}\n"
    print "prime: #{prime.inspect}\n"
    print "interval vector: #{interval_vector.inspect}\n"
    print "invariance vector: #{invariance_vector.inspect}\n"
    print "huron ADC: #{huron[0]}  pearsons: #{huron[1]}\n"
    print "balzano coherence: "
    if is_strictly_coherent?
      print "strictly coherent\n"
    elsif is_coherent?
      print "coherent\n"
    else
      print "false\n"
    end
  end


#  def lilypond
#  
#  end
#  
#  def musicXML
#    
#  end

###############################################################################
  private

  #
  # Convert every pitch array to a binary representation, e.g.:
  # [0,2,4,8,10] -> 010100010101
  #           2^n:  BA9876543210
  # The smallest binary number is the most left-compact.
  #
  def most_left_compact(pcset_array)
    if !pcset_array.all? {|pcs| pcs.length == pcset_array[0].length}
      raise ArgumentError, "PCSet.most_left_compact: All PCSets must be of same cardinality", caller
    end
    zeroed_pitch_arrays = pcset_array.map {|pcs| pcs.zero.pitches}
    binaries = zeroed_pitch_arrays.map {|array| array_to_binary(array)}
    winners = []
    binaries.each_with_index do |num, i|
      if num == binaries.min then winners.push(pcset_array[i]) end
    end
    winners.sort[0]
  end

end

我称呼他们如下:

> my_pcset = PCSet.new([0,2,4,6,8,10])
> my_pcset2 = PCSet.new([1,5,9])

应该返回:

> my_pcset = PCSet.new([0,2,4,6,8,10])
=> [0, 2, 4, 6, 8, 10]
> my_pcset2 = PCSet.new([1,5,9])
=> [1, 5, 9]

但是什么也不返回。该代码在github上可用谢谢

ruby algorithm require
2个回答
0
投票

0
投票
[的结果

my_pcset = PCSet.new([0,2,4,6,8,10])

应该将my_pcset设置为PCSet的实例而不是数组,因此README文件中的这些行充其量是令人困惑的。

3。使用方法

制作新的PCSet:

my_pcset = PCSet.new([0,2,4,6,8,10])

=> [0,2,4,6,8,10]

my_pcset2 = PCSet.new([1,5,9])

=> [1、5、9]

查看代码,我看到inspect已委派给@pitches

def inspect @pitches.inspect end

我认为如果您检查my_pcset,将会得到预期的结果。

my_pcset = PCSet.new([0,2,4,6,8,10])
p my_pcset # will print [0, 2, 4, 6, 8, 10]

or `my_pcset.inspect` will return what you are expecting.
© www.soinside.com 2019 - 2024. All rights reserved.