Применить ко всем элементам в списке в Haskell

#haskell

#haskell

Вопрос:

Я пытаюсь вычислить среднее значение элементов списка. Учитывая текстовый текст, где у меня есть имя, а затем числа для вычисления среднего:

 Bob 5 6 9 10 5
Alice 10 3 5 6 8
Kate 2 4
  

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

Я пытаюсь создать список кортежей для вычисления среднего значения с помощью функции composeList, но я получаю только первый элемент:

 [("Bob",[5.0,6.0,9.0,10.0,5.0])]
  

Я не знаю, как получить список кортежей, а затем вычислить среднее значение, чтобы получить

[(«Боб», 7.0), («Алиса», 6.5), «Кейт», 7.75)]

Вот что у меня есть до сих пор:

 import qualified Data.Text    as Text
import qualified Data.Text.IO as Text
import Data.String



readLines :: FilePath -> IO [String]
readLines = fmap lines . readFile 

calculateMean :: [Double] -> Double
calculateMean [] = 0
calculateMean (a) = sum a / fromIntegral(length a)


--calculateMeanList :: [String] -> (String,[Double])
--calculateMeanList (a) = (head $ words $ head a, calculateMean $ convertNumeric $ tail $ words $ head $ a)

convertNumeric :: [String] -> [Double]
convertNumeric = map read


composeList :: [String] -> [(String, [Double])]
composeList (a) = [(head $ words $ head $ a, convertNumeric $ tail $ words $ head $ a)]


main = do
    
    ls <- readLines "puntuaciones.txt"
    
  
    print $ words $ head $ ls

    print $ composeList ls
  

Комментарии:

1. Сначала напишите функцию, которая может превратить одну пару ("Bob",[5.0,6.0,9.0,10.0,5.0]) в нужную ("Bob",7.0) . Должно быть что-то вроде foo (name, values) = (... , ...) . Затем map поверх списка пар.

Ответ №1:

Вы берете только слова head , поэтому, конечно, вы получаете только первый элемент. Вы должны отображать words функцию по строкам:

 import qualified Data.Text    as Text
import qualified Data.Text.IO as Text
import Data.String

readLines :: FilePath -> IO [String]
readLines = fmap lines . readFile

calculateMean :: [Double] -> Double
calculateMean [] = 0
calculateMean (a) = sum a / fromIntegral(length a)

convertNumeric :: [String] -> [Double]
convertNumeric = map read


composeList :: [String] -> [(String, [Double])]
-- could be written with a map but list comprehension is cleaner
composeList a = [(head curWords, convertNumeric $ tail curWords) | curWords <- map words a]

main = do
    ls <- readLines "puntuaciones.txt"
    let composed = composeList ls
    print composed
    -- this could also be written with a list comprehension
    -- [(a, calculateMean b) | (a, b) <- composed]
    print $ map ((a, b) -> (a, calculateMean b)) composed
  

Комментарии:

1. Хорошо, я вижу, что я делал неправильно. Спасибо! Я пытался создать функцию для этого и заменить созданную вами лямбда-функцию, но, думаю, я тоже делаю это неправильно: calculateMeanList2 :: (String,[Double]) -> (String, Double) calculateMeanList2 (a, b) = map (l -> ((a, calculateMean b) l))

2. Кроме того, ваша лямбда неверна, потому что вам нужно сохранить первый элемент кортежа вне карты, а затем просто взять среднее значение списка, чтобы оно выглядело как calculateMeanList2 (a, b) = (a, calculateMean b)

3. Но есть ли способ вызвать map для этой функции calculateMeanList2? Я пытаюсь инкапсулировать все это в функцию.

4. calculateMeanList2 просто работает с кортежем. Затем вы можете просто map calculateMeanList2 composed изменить полный список.