Я попытался объединить решение sclv с комментарием luqui о unamb
и хотел бы поделиться своими результатами.Я начну с тестовых случаев:
list1 = [True, True, undefined, True, undefined]
list2 = [undefined, False, False]
list3 = concat $ replicate 500 list1
list4 = concat $ replicate 500 list2
main = mapM (print . vote) [list1, list2, list3, list4]
vote :: [Bool] -> Bool
Это должно вывести
True
False
True
False
Сначала я начну с примера list1
.Функция голосования для ее передачи может выглядеть следующим образом:
voteByTrue list = sum (map bToNat list) >= threshold
where
threshold = (genericLength list + 1) `quot` 2
Это то же самое, что и в ответе sclv.Теперь нам нужно сделать sum
более ленивым, чтобы вычисление не прерывалось при обнаружении undefined
слагаемого.Мой первый взгляд на это был:
Zero |+ y = y
Succ x |+ y = Succ (x + y)
instance Num Nat where
x + y = (x |+ y) `lub` (y |+ x)
Здесь |+
является строгим оператором сложения в первом аргументе, а +
не является строгим в обоих аргументах.Он работал для примеров игрушек, как list1
, но производительность этого решения очень быстро ухудшается из-за экспоненциального увеличения числа потоков (посмотрите, как каждый +
порождает 2 потока, каждый из которых вызывает +
опять же, обычно с теми же аргументами).При такой производительности vote list3
завершается недостаточно быстро.Чтобы бороться с этим, я попытался нарушить контракт unamb
и реализовал следующую функцию:
-- | The same as unamb, but does not have the
-- 'agree unless bottom' precondition.
brokenUnamb = unamb
infoMinMax a b = (x, y)
where
~(x, y) = (a `seq` (b, a)) `brokenUnamb` (b `seq` (a, b))
Эта функция сортирует два аргумента по количеству информации, которой они располагают.Он всегда возвращает менее оцененное значение как x
и более оцененное значение как y
.Это нарушает чистоту, нарушая условие unamb
аргументов, чтобы быть равными.Тем не менее, это позволяет нам реализовать +
более эффективно:
instance Num Nat where
x + y = x' |+ y' where (y', x') = infoMinMax x y
Это позволяет нам пройти большой тест (list3
)!Теперь о ложных тестах ... Оказалось, что функция infoMinMax
здесь тоже полезна!
vote list = voteByTrue list `maxInfo` voteByFalse list
where
voteByFalse = not . voteByTrue . map not
maxInfo x y = snd (infoMinMax x y)
Теперь это позволяет программе пройти все четыре теста, хотя и большиенесколько секунд, чтобы закончить.Загрузка процессора возрастает до 200%, если я заменим undefined
на odd (sum [1..])
, поэтому некоторый параллелизм действительно имеет место.
Однако проблема нарушенной чистоты остается.Может кто-нибудь предложить решение, где достаточно простого unamb
?