Преобразовать IO [Float] в [Float] в Haskell

Мне нужно преобразовать список IO [Float] в [Float]. Я получаю объект IO [Float] из следующей функции:

probs :: Int -> IO [Float]
probs 0 = return []
probs n = do
             p <- getStdRandom random
             ps <- probs (n-1) 
             return (p:ps)

Я понимаю, что результатом этой функции является список типа [IO Float], а не список чисел. Это список действий ввода-вывода, генерирующих числа. Ввод-вывод еще не был выполнен, поэтому в моем случае генератор случайных чисел фактически не сгенерировал числа. Я хочу сгенерировать каждое случайное число из содержимого этого списка, чтобы получить список [Float].

Мне нужно это, чтобы вычислить количество чисел в каждом квартиле результата (чтобы проверить распределение среди случайных чисел):

calcQuartile :: [Float] -> [Float] -> [Int]
calcQuartile randomList (x1:x2:rest) = length(filter (\x -> x>=x1 && x<x2) randomList):calcQuartile randomList (x2:rest)
calcQuartile x y = []

Я использую следующий код для запуска этой функции, которая не работает:

calcQuartile (probs x) [0,0.25..1]

Ошибка, которую я получаю:

 • Couldn't match expected type ‘[Float]’
               with actual type ‘IO [Float]’
 • In the first argument of ‘calcQuartile’, namely ‘(probs x)’
   In the expression: calcQuartile (probs x) [0, 0.25 .. 1]
   In an equation for ‘getAmountInQuartile’:
       getAmountInQuartile x = calcQuartile (probs x) [0, 0.25 .. 1]

Идея состоит в том, что нет разворачивает значения IO. Есть некоторые функции unsafePerformIO и его друзей, но это может привести к серьезным проблемам. Один пишет чистые функции, и вы можете, например, затем связать две функции вместе, например, с probs x >>= \y -> return (calcQuartile y [0, 0.25..1])

Willem Van Onsem 13.09.2018 11:14

На практике вы просто напишете (`calcQuartile`[0, 1/4 .. 1]) <$> probs x. (Назначение обратных кавычек - частично применить к правильному аргументу. Было бы проще, если бы вы сначала определили calcQuartile с перевернутыми аргументами, что упростило бы приложение до calcQuartile [0, 1/4 .. 1] <$> probs x.) Альтернатива: calcQuartile <$> probs x <*> pure [0, 1/4 .. 1].

leftaroundabout 13.09.2018 11:36

Кстати, для более сложных вещей со случайными числами есть специальные монады, так что вы можете вообще не делать все в IO. hackage.haskell.org/package/random-fu-0.2.7.0/docs/…

leftaroundabout 13.09.2018 11:44

@leftaroundabout Я должен сказать, что не считаю свой вопрос дубликатом связанных вопросов, поскольку в моем вопросе явно указано, что он касается IO список и имеет дело со значениями случайный. Я надеялся на какое-то решение, которое конкретно касается списков монад (способы fold вместо монадического списка (возможно, foldM?) Или map или filter или что-то еще). То, что ответ на этот вопрос теперь аналогичен ответам на другой вопрос, не означает, что я ожидал найти аналогичные ответы.

Simon Baars 13.09.2018 21:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
681
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Обновлено: как отмечалось в стиле @freestyle, это решение работает, но может привести к непредсказуемым результатам:

I imported the module System.IO.Unsafe:

import System.IO.Unsafe

Next, I can replace my code by the following:

calcQuartile (unsafePerformIO(probs x)) [0,0.25..1]

Лучшее решение, как отмечает @Willem Van Onsem, следующее:

probs x >>= \y -> return (calcQuartile y [0, 0.25..1])

Примечательно, однако, что это возвращает IO [Int] вместо [Int]. монадическое связывание>>= связывает два нечистых действия вместе (то есть в данном случае действия ввода-вывода) и делает из них одно действие IO. То же самое можно записать и в нотации do:

do
  y <- probs x
  return $ calcQuartile y [0, 0.25 .. 1]

В этом конкретном случае вторым действием (calcQuartile) на самом деле является чистый (таким образом, return, который обертывает чистое значение как действие ввода-вывода без побочных эффектов), поэтому вам вообще не нужен экземпляр монады, и вы можете вместо этого просто используйте IO как функтор:

fmap (\y -> calcQuartile y [0, 0.25..1]) $ probs x

или

(`calcQuartile`[0, 0.25..1]) <$> probs x

Да, работает, но как это работает? Можете ли вы предсказать и объяснить результат: let {a = calcQuartile (unsafePerformIO(probs x)) [0,0.25..1]; b = calcQuartile (unsafePerformIO(probs x)) [0,0.25..1]} in a == b

freestyle 13.09.2018 11:10

@freestyle Но разве это не все при работе со случайными числами? Случайные результаты не совсем предсказуемы по своей природе. Как вы могли бы предложить этот вариант использования более "чистым" способом?

Simon Baars 13.09.2018 11:16

@SimonBaars Если вы включите оптимизацию, код фристайла внезапно вернет True, а не False.

sepp2k 13.09.2018 11:20

Это явно нет безопасное использование unsafePerformIO. Как говорит фристайл, это нарушает ссылочную прозрачность: если вы хотите проверить несколько разных образцов из одного и того же дистрибутива, вы ожидаете всегда получать немного разные результаты, но unsafePerformIO (probs x) семантически является просто константой. Как правило, никогда не используйте unsafePerformIO. (В тех немногих случаях, когда это действительно уместно, вы должны быть на 100% уверены и не должны задавать по этому поводу вопросов.) Определенно не используйте его, если вы еще не очень хорошо разбираетесь в монадах.

leftaroundabout 13.09.2018 11:29

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

leftaroundabout 13.09.2018 13:28

@leftaroundabout Извините ... Я не знал о непредсказуемых результатах. Я отредактировал свой ответ, чтобы люди, которые задают этот вопрос, не могли прийти к неверным практическим решениям.

Simon Baars 13.09.2018 14:38

@leftaroundabout Большое спасибо за подробный ответ :-).

Simon Baars 13.09.2018 21:06

Другие вопросы по теме