Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Ruby: Общие вопросы > Array#uniq


Автор: valodzka 10.11.2006, 01:43
Почему неправильно работает этот код? 
Код

irb(main):019:0> [{1=>1}, {1=>1}].uniq
=> [{1=>1}, {1=>1}]

Автор: setq 11.11.2006, 10:08
Может быть он использует проверку .equal? ?

Автор: Pete 11.11.2006, 11:11
Код

D:\Work>irb
irb(main):001:0> a = [ 1, 1 ]
=> [1, 1]
irb(main):002:0> p a[0].object_id, a[1].object_id
3
3
=> nil
irb(main):003:0> a = [ {1 => 1}, {1 => 1} ]
=> [{1=>1}, {1=>1}]
irb(main):004:0> p a[0].object_id, a[1].object_id
23577508
23577496
=> nil
irb(main):005:0>

Автор: FunnyFalcon 13.11.2006, 13:09
Вопрос остаётся открытым!!!
Код

yura@falcon:~$ irb
irb(main):001:0> a = [ 1, 1 ]
=> [1, 1]
irb(main):002:0> p a[0].object_id, a[1].object_id
3
3
=> nil
irb(main):003:0> a = [ [1 , 2], [1 , 2] ]
=> [[1, 2], [1, 2]]
irb(main):004:0> p a[0].object_id, a[1].object_id
23577508
23577496
=> nil
irb(main):005:0> a.uniq
=> [[1, 2]]
irb(main):006:0> a = [ {1 , 2}, {1 , 2} ]
=> [{1=>2}, {1=>2}]
irb(main):007:0> a.uniq
=> [{1=>2}, {1=>2}]


Добавлено @ 13:14 
Array вычисляет хэш от аргументов.
Hash возвращяет object_id  smile 
Код

yura@falcon:~/Programming/Ruby/ruby-1.8.5$ irb
irb(main):001:0> [1, 2].hash
=> 11
irb(main):002:0> [[1, 2],[1, 2]].map{|a| a.hash}
=> [11, 11]
irb(main):003:0> [{1, 2},{1, 2}].map{|a| a.hash}
=> [-605969496, -605969506]
irb(main):004:0>

Автор: Bikutoru 16.11.2006, 12:54
А как вам такое решение?
Код

class Hash
  def eql?(obj)
    size == keys.find_all { |key| self[key] == obj[key] }.size
  end

  def hash
    all_hashes = []
    ObjectSpace.each_object(Hash) { |obj| all_hashes << obj }
    all_hashes.sort { |a, b| a.object_id <=> b.object_id }.each do |obj|
      return obj.object_id if eql? obj
    end

    object_id
  end
end

puts [{}, {}].uniq.size # 1
puts [{"abc" => 2, 1 => 3}, {1 => 3, "abc" => 2}].uniq.size # 1
puts [{:a => 1}, {:a => 1}, {"b" => 123}].uniq.size # 2


Добавлено @ 12:58 
Хотя, наверное, проще будет написать свой uniq...

Автор: FunnyFalcon 16.11.2006, 22:43
Вариант
Код

class Hash
  alias eql? :==
  def hash
     to_a.sort! do |a,b|
          a, b  = a[0], b[0]
          a<=>b || a.class <=> b.class || a.class.object_id <=> b.class.object_id
          # Сортировка аля Python.
     end.hash
  end
end


Автор: Rubynovich 18.11.2006, 14:11
Код

[{1=>1}, {1=>1}].map{ |hash| hash.to_a[0] }.uniq.map{ |array| Hash[ *array ] }

А вообще, задача немножко бредовая... смущают меня эти пары аля хеш... Уникальность для хешей... странно это все как-то...

Автор: valodzka 22.11.2006, 18:30
Цитата(Rubynovich @ 18.11.2006,  14:11)
А вообще, задача немножко бредовая... смущают меня эти пары аля хеш... Уникальность для хешей... странно это все как-то...

Если интересно зачем это реализовывалось: программка осуществляет логический вывод по аналогии, результат хранится ввиде хэша {Переменная=>Константа*}, по ходу вывода возникают несколько результатов, но некоторые могут совпадать. Быстро удалить не получилось, пришлось реализовывать свой uniq. Но уже просто интересно стало что за ерунда.

Автор: Rubynovich 26.11.2006, 22:48
valodzka, метод .uniq для хеш -- это носенс. В нем и так все ключи должны быть уникальными. А задача, которую вы решаете, делается методом .update.

Автор: valodzka 27.11.2006, 14:15
Цитата(Rubynovich @ 26.11.2006,  22:48)
valodzka, метод .uniq для хеш -- это носенс. В нем и так все ключи должны быть уникальными. А задача, которую вы решаете, делается методом .update.

Непонял...

.uniq это для массива хешей, в котором могут содержаться абсолютно одинаковые  хэши. И чем здесь мог момочь .update? 

Автор: V.A.KeRneL 10.12.2006, 12:02
Цитата(FunnyFalcon @ 16.11.2006,  22:43)

Вариант
Код

class Hash
  alias eql? :==
  def hash
     to_a.sort! do |a,b|
          a, b  = a[0], b[0]
          a<=>b || a.class <=> b.class || a.class.object_id <=> b.class.object_id
          # Сортировка аля Python.
     end.hash
  end
end


Спасибо за вариант, FunnyFalcon.
Насколько я понял, Вы программист на Python. И в мире Ruby относительный новичок. Посему позволю себе Вас поправить.

Ну, во-первых, это как-то странно: 
Код

a, b  = a[0], b[0]

Я понимаю, что переменные `a' и `b' локальные и определены только внутри данного блока кода, да и корректному выполнению метода данное присваивание не помешает, но для меня всё же это сродни присваиванию чего-либо счётчику цикла. Целей для такого присваивания может быть 2: 
1) Меньше работать ручками. Тогда Вы мало что выиграли, нужен больший, больший чем 1 строка, кусок кода.
2) Сделать код более читаемым. Это приемлемо. В таком случае, мне кажется, более правильным тут же локально завести новые переменные. Например: 
Код

k, o  = a[0], b[0]


Во-вторых, с учётом 1-го пункта, строку 
Код

a<=>b || a.class <=> b.class || a.class.object_id

надо переписать так: 
Код

(a<=>b).nonzero? || (a.class <=> b.class).nonzero? || a.class.object_id

, потому что в Ruby 0 -- это истина, как и всё, кроме false и nil.

Ну и последнее, Вы совершенно напрасно использовали метод Array#sort!
Тут, очевидно, подразумевался метод Array#sort
Метод Array#sort! я вообще практически не использую. Его можно использовать только в том случае, если Вы хотите отсортировать какую-то свою ПЕРЕМЕННУЮ-массив: 
Код

arr.sort! { |a, b| conditions... }

И даже тогда лучше это сделать при помощи явного присваивания: 
Код

arr = arr.sort { |a, b| conditions... }

А так все старания по модификации самого объекта тратятся впустую!

Добавлено @ 12:15 
Цитата(FunnyFalcon @ 13.11.2006,  13:09)

Добавлено @ 13:14 
Array вычисляет хэш от аргументов.
Hash возвращяет object_id  smile 
Код

yura@falcon:~/Programming/Ruby/ruby-1.8.5$ irb
irb(main):001:0> [1, 2].hash
=> 11
irb(main):002:0> [[1, 2],[1, 2]].map{|a| a.hash}
=> [11, 11]
irb(main):003:0> [{1, 2},{1, 2}].map{|a| a.hash}
=> [-605969496, -605969506]
irb(main):004:0>


Оу, FunnyFalcon, а я сначала не заметил, что это тоже Вы написали. И в голову не пришло! smile Возможно, придётся взять обратно слова по поводу новичка в Ruby. smile

Единственное, что я здесь не пойму так это: 
Код

=> [-605969496, -605969506]

Отрицательные идентификаторы -- это особенность версии 1.8.5 (у меня 1.8.2 и результаты как у Pete, у него, по-моему, 1.8.4) или сборки под *nix?

Автор: V.A.KeRneL 11.12.2006, 03:01
Кстати, FunnyFalcon, зачем в Вашем решении использовалась сортировка, я так и не понял! smile
Да ещё «аля Python»! smile

Выстраданное решение: 
Код

class Hash
    alias eql? :==
    def hash
        to_a.hash
    end
end

puts [{}, {}].uniq.size # 1
puts [{"abc" => 2, 1 => 3}, {1 => 3, "abc" => 2}].uniq.size # 1
puts [{:a => 1}, {:a => 1}, {"b" => 123}].uniq.size # 2


Добавлено @ 03:13 
В тему: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/16778
Еcть идеи насчёт?..

Автор: V.A.KeRneL 11.12.2006, 03:45
А вообще, о таких интересных вещах неплохо было бы спросить разработчиков... Есть смелые добровольцы с хорошим знанием английского/японского? smile

Автор: FunnyFalcon 11.12.2006, 12:35
Цитата

Код
Код

a<=>b || a.class <=> b.class || a.class.object_id

надо переписать так: 
Код
Код

(a<=>b).nonzero? || (a.class <=> b.class).nonzero? || a.class.object_id

, потому что в Ruby 0 -- это истина, как и всё, кроме false и nil.


Спасибо, вы не правы. Я написал именно то, что хотел:
если два ключа сравнимы, то использовать результат их сравнения, 
иначе (когда a<=>b => nil !!!!), попытаться сравнить их классы,
если и классы не сравнимы, то сравнить классы по object_id.
В Python любые два объекта сравнимы именно по такому алгоритму, даже если они не связанных друг с другом классов.
В Ruby же результат сравнения может быть не определён, если классы сравниваемых объектов отношения друг к другу не имеют.

Цитата

Метод Array#sort! я вообще практически не использую. Его можно использовать только в том случае, если Вы хотите отсортировать какую-то свою ПЕРЕМЕННУЮ-массив


Метод sort! можно применять к любому объекту-массиву, чье состояние до сортировки вас абсолютно не интересует.
В данном случае, массив создается и возвращается методом to_a, поэтому я абсолютно уверен, что этот же объект-массив никто больше
не увидит, и я могу отсортировать его на месте, сэкономив память. 

Впрочем - это дело принципа. Мало ли кто унаследует от Hash и захочет переопределить to_a, чтобы он возвращал кешированное значение. Есть безумцы в этом мире. Если учитывать такую вероятность, то вы абсолютно правы.

Цитата

Код

    def hash
        to_a.hash
    end



Я тоже думал так. Но кто гарантирует, что два хеша с одинаковыми ключами вернут методом to_a массив, в котором ключи будут в одинаковом порядке.
Т.е. Вы уверены, что для двух разных объектов - хешей, содержимое которых {1=>2, "2"=>"1"} метод to_a не вернет два разных массива: 
[[1, 2], ["2", "1"]] и [["2","1"],[1,2]] ?
Я не смотрел исходники реализации хэша в Ruby. И кроме того, существуют и другие реализации Ruby (JRuby например).
По-этому я не уверен, и подозреваю, что в общем случае to_a может вернуть разные массивы для равных хешей, если история их наполнения и изменения была разной!!! Поэтому я и сортирую массив по первому элементу - ключу в хэше.
Но Ruby не позволяет сортировать массив с элементами разных классов - см. выше. По-этому я и сортирую как в Python.

Автор: Pete 13.12.2006, 00:52
Цитата(V_A_KeRneL @  11.12.2006,  04:45 Найти цитируемый пост)
неплохо было бы спросить разработчиков

Ну, понеслась... А все из-за какого-то простого uniq().
Напомнило тему в разделе Си http://forum.vingrad.ru/index.php?showtopic=37913&view=findpost&p=285338...

Добавлено @ 00:53 
 smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)