Understanding the source code for Ruby's repea

2019-05-22 15:24发布

I've been building an intelligent Mastermind game in Ruby. In my game, if you select the option of having the computer play the role of code breaker, the computer makes educated guesses at what the code maker's code is.

As part of my algorithm, the computer first looks at the entire list of ALL POSSIBLE CODES.

For example, if there are 6 colors to choose from (red orange blue green purple yellow) and the code is made up of 4 colors (repeats are allowed), then to view all possible codes, you can do this:

valid_colors = %w(red orange blue green purple yellow)
all_possible_codes = valid_colors.repeated_permutation(4).to_a

And all_possible_codes will be an array filled with arrays representing every possible code. The computer then eliminates codes from this list as it gets feedback from each of its guesses.

The next thing I'm working on, however, requires me to use JRuby 1.6.6, which uses Ruby 1.8.7, which does NOT have a repeated_permutation method. I need to write my own method with the same functionality.

So I went to the source code found here: http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-repeated_permutation

Unfortunately, I don't understand what they're doing or how I could go about solving this by writing a method of my own. I'm fairly new to programming and haven't been able to figure this out. Any help with understanding that source code would be greatly appreciated!

2条回答
beautiful°
2楼-- · 2019-05-22 16:03

Thanks yngum!

Just to expand on your answer, I renamed some variables to make it more Ruby-esque, more clear, descriptive, and explicit. I also changed a few things so the return value of the method would be the answer, an array that contains each of the possible permutations.

def repeated_permutations(original_array, length_of_each_permutation, list_of_permutations, index_positions, index)
    0.upto(original_array.length - 1) do |index1|
        index_positions[index] = index1
        if index < length_of_each_permutation - 1
            repeated_permutations(original_array, length_of_each_permutation, list_of_permutations, index_positions, index + 1)
        else
            permutation = Array.new
            0.upto(length_of_each_permutation - 1) do |index2|
                permutation[index2] = original_array[index_positions[index2]]
            end 
            list_of_permutations.push(permutation)
        end
    end
    list_of_permutations
end

I don't love naming it index_positions, but couldn't think of a better name. The above method will return an array filled with arrays of all possible permutations with repetition allowed.

To implement:

valid_colors = %w(red orange blue green purple yellow) repeated_permutations(valid_colors, 4, [], [], 0)

查看更多
乱世女痞
3楼-- · 2019-05-22 16:22

The code you linked calls rpermute0 which does most the work, source code for rpermute0 in Array.c is

static void
rpermute0(long n, long r, long *p, long index, VALUE values)
{
    long i, j;
    for (i = 0; i < n; i++) {
    p[index] = i;
    if (index < r-1) {              /* if not done yet */
        rpermute0(n, r, p, index+1, values); /* recurse */
    }
    else {
        /* We have a complete permutation of array indexes */
        /* Build a ruby array of the corresponding values */
        /* And yield it to the associated block */
        VALUE result = rb_ary_new2(r);
        VALUE *result_array = RARRAY_PTR(result);
        const VALUE *values_array = RARRAY_PTR(values);

        for (j = 0; j < r; j++) result_array[j] = values_array[p[j]];
        ARY_SET_LEN(result, r);
        rb_yield(result);
        if (RBASIC(values)->klass) {
        rb_raise(rb_eRuntimeError, "repeated permute reentered");
        }
    }
    }
}

Basially a brute force, starting from 0 returning one permutation each iteration. ruby version is something like

require 'pp'

def rpermute(numRepeat, pArray, index, originArray)
  0.upto(originArray.length-1) do |i|
    pArray[index] = i
    if index < numRepeat-1
      rpermute(numRepeat, pArray, index+1, originArray)
    else
      result = Array.new
      0.upto(numRepeat-1) do |j|
        result[j] = originArray[pArray[j]]
      end
     pp result
    end
  end
end

originArray1 = [1,2,3,4,5]
originArray2 = ['a','b','c','d','e']
pArray = []

rpermute(4, pArray, 0, originArray1)
rpermute(4, pArray, 0, originArray2)

I tested above code and it prints out all permutation of length 4, u might want to put them into array.

查看更多
登录 后发表回答