Обратите внимание, что в вышеупомянутых решениях для C++, были некоторые проблемы.
Одно решение было неэффективно, потому что оно передало станд.:: строка копией, и потому что это выполнило итерации по всем символам, вместо того, чтобы сравнить только половину символов. Затем даже когда обнаружение строки не было палиндромом, это продолжало цикл, ожидая его конец прежде, чем сообщить о "лжи".
другой было лучше, с очень небольшой функцией, проблема которой состояла в том, что она не смогла протестировать что-либо еще, чем станд.:: строка. В C++ легко расширить алгоритм до целого набора подобных объектов. Путем шаблонной обработки его станд.:: строка в "T", это работало бы над обоими станд.:: строка, станд.:: wstring, станд.:: вектор и станд.:: двухсторонняя очередь. Но без основной модификации из-за использования оператора < станд.:: список был вне его объема.
Мои собственные решения пытаются показать, что решение для C++ не остановится при работе над точным текущим типом, но будет стремиться работать что-либо , который ведет себя тот же путь, неважно, тип. Например, я мог применить свои тесты палиндрома на станд.:: строка, на векторе интервала или в списке "Чего-либо", пока Что-либо было сопоставимо через его оператор = (сборка в типах, а также классы).
Примечание, что шаблон может даже быть расширен с помощью дополнительного типа, который может использоваться для сравнения данных. Например, если Вы хотите выдержать сравнение нечувствительным к регистру способом, или даже сравнить подобные символы (как ГЁ, Г©, Г «, ГЄ и e).
Как король Leonidas сказал бы: "Шаблоны? Это - C++!!!"
Так, в C++, существует по крайней мере 3 главных способа сделать это, каждый ведущий к другому:
проблема состоит в том, что до C++ 0X, мы не можем рассмотреть станд.:: массив строк символов как непрерывные, таким образом, мы должны "обмануть" и получить c_str () свойство. Поскольку мы используем его способом только для чтения, это должно быть в порядке...
<час>bool isPalindromeA(const std::string & p_strText)
{
if(p_strText.length() < 2) return true ;
const char * pStart = p_strText.c_str() ;
const char * pEnd = pStart + p_strText.length() - 1 ;
for(; pStart < pEnd; ++pStart, --pEnd)
{
if(*pStart != *pEnd)
{
return false ;
}
}
return true ;
}
<час> "C++" Теперь, мы попытаемся применить то же решение, но к любому контейнеру C++ с произвольным доступом к его объектам через оператор []. Например, любой станд.:: basic_string, станд.:: вектор, станд.:: двухсторонняя очередь, и т.д. Оператор [] является постоянным доступом для тех контейнеров, таким образом, мы не потеряем неуместную скорость.
<час>template <typename T>
bool isPalindromeB(const T & p_aText)
{
if(p_aText.empty()) return true ;
typename T::size_type iStart = 0 ;
typename T::size_type iEnd = p_aText.size() - 1 ;
for(; iStart < iEnd; ++iStart, --iEnd)
{
if(p_aText[iStart] != p_aText[iEnd])
{
return false ;
}
}
return true ;
}
<час> Это будет работать почти с любым незаказанным подобным STL контейнером с двунаправленными итераторами, Например, любым станд.:: basic_string, станд.:: вектор, станд.:: двухсторонняя очередь, станд.:: список, и т.д. Так, эта функция может быть применена на все подобные STL контейнеры со следующими условиями: 1 - T является контейнером с двунаправленным итератором 2 - итератор T указывает на сопоставимый тип (через оператор =)
<час>template <typename T>
bool isPalindromeC(const T & p_aText)
{
if(p_aText.empty()) return true ;
typename T::const_iterator pStart = p_aText.begin() ;
typename T::const_iterator pEnd = p_aText.end() ;
--pEnd ;
while(true)
{
if(*pStart != *pEnd)
{
return false ;
}
if((pStart == pEnd) || (++pStart == pEnd))
{
return true ;
}
--pEnd ;
}
}
<час> Если все, что вас волнует, это уникальность element2, вы можете просто сделать:
element2.to_a - element1.to_a
Изменить:
Я постоянно возвращаюсь к этому коду, чтобы использовать его в проектах, над которыми я работаю. Вот последняя версия, которая полезна для глубоко вложенных структур и основана на приведенном выше коде Пита. Обычно я помещаю его в config / initializers / core_ext.rb (в проекте Rails):
class Hash
def deep_diff(other)
(self.keys + other.keys).uniq.inject({}) do |memo, key|
left = self[key]
right = other[key]
next memo if left == right
if left.respond_to?(:deep_diff) && right.respond_to?(:deep_diff)
memo[key] = left.deep_diff(right)
else
memo[key] = [left, right]
end
memo
end
end
end
class Array
def deep_diff(array)
largest = [self.count, array.count].max
memo = {}
0.upto(largest - 1) do |index|
left = self[index]
right = array[index]
next if left == right
if left.respond_to?(:deep_diff) && right.respond_to?(:deep_diff)
memo[index] = left.deep_diff(right)
else
memo[index] = [left, right]
end
end
memo
end
end
Вот небольшая демонстрация:
> {a: [{b: "c", d: "e"}, {b: "c", f: "g"}]}.deep_diff({a: [{b: "c", d: "e"}, {b: "d", f: "g"}]})
=> {:a=>{1=>{:b=>["c", "d"]}}}
Старый ответ:
Я обнаружил, что метод Rails Hash diff не говорит мне, что был слева и справа (что гораздо полезнее). Был исчезнувший с тех пор плагин с названием "Riff", который позволял вам различать два объекта ActiveRecord. По сути:
class Hash
def diff(other)
self.keys.inject({}) do |memo, key|
unless self[key] == other[key]
memo[key] = [self[key], other[key]]
end
memo
end
end
end