Nano Hash - криптовалюты, майнинг, программирование

Как выйти из функции fold в haskell, когда аккумулятор выполнил определенное условие?

Я вычисляю сумму списка после применения someFunction к каждому его элементу следующим образом:

sum (map someFunction myList)

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

Вроде как нужно использовать фолд, но я не знаю, как его выбить, если аккум достигает порога. Я предполагаю, что нужно каким-то образом составить fold и takeWhile, но я не совсем уверен, как это сделать.


  • Почти, но используйте scanl вместо fold. 07.08.2018
  • Если сумма превысит порог или если результат вашей ресурсоемкой функции превысит порог? 08.08.2018

Ответы:


1

Одним из вариантов может быть использование scanl функция, которая возвращает список промежуточных вычислений foldl.

Таким образом, scanl1 (+) (map someFunction myList) вернет промежуточные суммы ваших вычислений. А поскольку Haskell — ленивый язык, он не будет вычислять все значения myList, пока вам это не понадобится. Например:

take 5 $ scanl1 (+) (map someFunction myList)

вычислит someFunction 5 раз и вернет список этих 5 результатов.

После этого вы можете использовать либо takeWhile или dropWhile и остановите расчет, когда определенное условие True. Например:

head $ dropWhile (< 1000) $ scanl1 (+) [1..1000000000]

остановит расчет, когда сумма чисел достигнет 1000 и вернет 1035.

07.08.2018
  • Возможно, следует принять во внимание, что порог никогда не будет достигнут, и в этом случае head выдаст ошибку. 07.08.2018
  • может быть, я бы использовал find вместо head и dropWhile: find (>= 1000) (scanl1 (+) [1..]) 08.12.2020

  • 2

    Другой метод заключается в использовании foldM с Either для захвата эффекта досрочного завершения. Left сигнализирует о досрочном прекращении.

    import Control.Monad(foldM)
    
    sumSome :: (Num n,Ord n) => n -> [n] -> Either n n
    sumSome thresh = foldM f 0
      where
        f a n 
          | a >= thresh = Left a
          | otherwise   = Right (a+n)
    

    Чтобы игнорировать условие выхода, просто напишите с помощью either id id.

    sumSome' :: (Num n,Ord n) => n -> [n] -> n
    sumSome' n = either id id . sumSome n
    
    07.08.2018

    3

    Используйте ограниченный оператор сложения вместо (+) с foldl.

    foldl (\b a -> b + if b > someThreshold then 0 else a) 0 (map someFunction myList)
    

    Поскольку Haskell не является строгим, оцениваются только те вызовы someFunction, которые необходимы для оценки if-then-else. fold по-прежнему обходит весь список.

    > foldl (\b a -> b + if b > 10 then 0 else a) 0 (map (trace "foo") [1..20])
    foo
    foo
    foo
    foo
    foo
    15
    

    sum [1..5] > 10, и вы можете видеть, что trace "foo" выполняется только 5 раз, а не 20.

    Однако вместо foldl следует использовать строгую версию foldl' из Data.Foldable..

    07.08.2018
  • Это круто и похоже на колдовство, но применительно к бесконечному списку это зависает. Я думаю, что трассировка показывает, что a просто не принудительно, хотя весь список пройден. 07.08.2018
  • Ах, верно. Я не мог понять, почему это все равно работает. 07.08.2018
  • Я думал, что этот трюк с ленью — канонический способ вырваться из складок. 08.08.2018

  • 4

    Вы можете попробовать создать свою собственную функцию sum, возможно, назовите ее boundedSum, которая принимает

    1. Integer верхняя граница

    2. [Integer] для суммирования

    3. значение "суммировать до этой точки" для сравнения с верхней границей

    и возвращает сумму списка.

        boundedSum :: Integer -> [Integer] -> Integer -> Integer
        boundedSum upperBound (x : xs) prevSum =
            let currentSum = prevSum + x
                in
            if   currentSum > upperBound
            then upperBound
            else boundedSum upperBound xs currentSum
        boundedSum upperBound [] prevSum =
            prevSum
    

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

    EDIT: ответы на этот вопрос предлагает лучшие методы, чем мой, и сам вопрос очень похож на ваш.

    07.08.2018

    5

    Это сделает то, о чем вы спрашиваете, без создания промежуточного списка, как scanl'scanl даже вызовет наращивание переходников поверх этого):

    foldl'Breaking break reduced reducer acc list = 
        foldr cons (\acc -> acc) list acc 
              where 
              cons x r acc | break acc x = reduced acc x 
                           | otherwise   = r $! reducer acc x
    

    ср. связанная вики-страница.

    07.08.2018

    6

    Это возможное решение:

    last . takeWhile (<=100) . scanl (+) 0 . map (^2) $ [1..]
    

    Рассеченный:

    • возьмите свой стартовый список ([1..] в примере)
    • сопоставьте свою дорогую функцию ((^2))
    • вычислять частичные суммы scanl (+) 0
    • остановитесь после того, как частичные суммы станут слишком большими (сохраните эти (<=100))
    • взять последний

    Если производительность имеет значение, также попробуйте scanl', которая может ее улучшить.

    07.08.2018

    7

    Что-то вроде этого с использованием until :: (a -> Bool) -> (a -> a) -> a -> a из Prelude

    sumUntil :: Real a => a -> [a] -> a
    sumUntil threshold u = result
    
        where
    
        (_, result) = until stopCondition next (u, 0)
    
        next :: Real a => ([a], a) -> ([a], a)
        next ((x:xs), y) = (xs, x + y)
    
        stopCondition :: Real a => ([a], a) -> Bool
        stopCondition (ls, x) = null ls || x > threshold
    

    Затем примените

    sumUntil 10 (map someFunction myList)
    
    08.08.2018
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..