Получение разных выходных данных от ручных и программных c массивов - PullRequest
0 голосов
/ 09 января 2020

Я получаю некоторые странные результаты, реализующие циклическую c перестановку дочерних элементов многомерного массива.

Когда я вручную определяю массив, например,

arr = [
  [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]
]

, вывод отличается с того момента, когда я получаю тот же массив, вызывая метод, который его создает.

Я сравнил ручной массив с созданной версией, и они абсолютно одинаковы (класс и значения и т. д. c).

Я пытался написать тот же алгоритм в JS и столкнулся с той же проблемой.

Есть идеи, что может происходить?

def Build_array(child_arr, n)
    #Creates larger array with arr as element, n times over. For example Build_array([1,2,3], 3) returns [[1,2,3], [1,2,3], [1,2,3]] 

    parent_arr = Array.new(4)

    0.upto(n) do |i|
        parent_arr[i] = child_arr
    end

    return parent_arr
end

def Cylce_child(arr, steps_tocycle)    
    # example: Cylce_child([1, 2, 3, 4, 5], 2) returns [4, 5, 1, 2, 3]

    0.upto(steps_tocycle - 1) do |i|
        x = arr.pop()
        arr.unshift(x)
    end

    return arr
end

def Permute_array(parent_array, x, y, z)
    #x, y, z = number of steps to cycle each child array

    parent_array[0] = Cylce_child(parent_array[0], x)
    parent_array[1] = Cylce_child(parent_array[1], y)
    parent_array[2] = Cylce_child(parent_array[2], z)

    return parent_array
end

arr = Build_array([1, 2, 3, 4, 5], 4)
# arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]


puts "#{Permute_array(arr, 1, 2, 3)}"


# Line 34: When arr = Build_array([1, 2, 3, 4, 5], 4) 
# Result (WRONG):
#  [[5, 1, 2, 3, 4], [5, 1, 2, 3, 4], [5, 1, 2, 3, 4], [5, 1, 2, 3, 4]]
#   
# Line 5: When arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, # 2, 3, 4, 5]]
# Result (CORRECT):
#   [[5, 1, 2, 3, 4], [4, 5, 1, 2, 3], [3, 4, 5, 1, 2], [1, 2, 3, 4, 5]]
#  

Ответы [ 3 ]

1 голос
/ 09 января 2020

Я вижу, где была ошибка. Добавил метод клонирования в строку 8, чтобы он теперь читал:

parent_arr[i] = child_arr.clone

#Old: parent_arr[i] = child_arr

Спасибо, Робин, за то, что указал мне правильное направление.

1 голос
/ 09 января 2020

Это довольно распространенная ошибка в Ruby, поскольку массивы не содержат сами по себе объекты, но ссылки на объекты , которые фактически являются указателями на динамически размещаемый объект, а не на сам объект.

Это означает, что этот код:

Array.new(4, [ ])

даст массив, содержащий четыре идентичные ссылки на один и тот же объект, причем этот объект является вторым аргументом.

Чтобы увидеть, что происходит:

Array.new(4, [ ]).map(&:object_id)
# => => [70127689565700, 70127689565700, 70127689565700, 70127689565700]

Обратите внимание на четыре идентичных идентификатора объекта. Тем более очевидно, если вы позвоните по этому поводу uniq.

Чтобы исправить это, вы должны предоставить блок, который каждый раз выдает другой объект:

Array.new(4) { [ ] }.map(&:object_id)
# => => [70127689538260, 70127689538240, 70127689538220, 70127689538200]

Теперь добавление к одному элементу не влияет на другие.

Тем не менее, в вашем коде есть много проблем, которые можно решить, используя Ruby, как это было задумано (например, больше "idiomati c). "code):

def build_array(child_arr, n)
  # Duplicate the object given each time to avoid referencing the same thing
  # N times. Each `dup` object is independent.
  Array.new(4) do
    child_arr.dup
  end
end

def cycle_child(arr, steps_tocycle)
  # Ruby has a rotate method built-in
  arr.rotate(steps_tocycle)
end

# Using varargs (*args) you can just loop over how many positions were given dynamically
def permute_array(parent_array, *args)
  # Zip is great for working with two arrays in parallel, they get "zippered" together.
  # Also map is what you use for transforming one array into another in a 1:1 mapping
  args.zip(parent_array).map do |a, p|
    # Rotate each element the right number of positions
    cycle_child(p, -a)
  end
end

arr = build_array([1, 2, 3, 4, 5], 4)
# => [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]

puts "#{permute_array(arr, 1, 2, 3)}"
# => [[5, 1, 2, 3, 4], [4, 5, 1, 2, 3], [3, 4, 5, 1, 2]]

Многие из этих методов сводятся к некоторым очень простым Ruby, поэтому они не особенно полезны сейчас, но это адаптирует код как можно более непосредственно для образовательных целей.

1 голос
/ 09 января 2020

Проблема в том, как вы строите массив.

Эта строка:

parent_arr[i] = child_arr

не помещает в parent_arr[i] копию child_arr, а ссылку на нее .

Это означает, что ваш исходный массив содержит четыре ссылки на один и тот же дочерний массив. Позже, когда код меняет parent_arr[0], он меняет тот же массив, на который ссылался child_arr в методе сборки. И этот массив также parent_arr[1] и parrent_arr[2] и т. Д.

Простое решение проблемы состоит в том, чтобы поместить в parent_arr[i] копию child_arr:

parent_arr[i] = Array.new(child_arr)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...