2012-04-06 3 views
8

Следующие две функции очень похожи. Они читают из [String] n элементов, либо [Int], либо [Float]. Как я могу определить общий код? Я не знаю какого-либо механизма в Haskell, который поддерживает передачу типов в качестве аргументов.Типы пропуска в качестве аргументов функции в Haskell?

readInts n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x :: Int 

readFloats n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x :: Float 

Я нахожусь на начальном уровне в Haskell, поэтому любые комментарии к моему коду приветствуются.

+2

Вам не нужно складывать здесь, вы можете получить с помощью простой карты. например 'map read stream :: [Int]' Также вы можете посмотреть, почему вы хотите использовать foldr в Haskell, а не foldl. –

+1

@EdwardKmett Спасибо за ваше предложение. Я действительно хочу читать только первые n элементов и возвращать список и остальную часть потока. Вчера я был супер сон, и не мог продумать. Я думаю, вы хотите сказать, что с помощью foldr я могу использовать конструктор: прямо справа? Позднее я переписал его как «(карта читала firstn, rest), где (firstn, rest) = splitAt n stream', очень похоже на то, что вы предложили. –

+0

Вам не нужно вставлять 'where'; вы можете поместить 'next (lst, x: xs) _ = ...' и 'v = ...' в последовательные строки. – sdcvvc

ответ

14

Haskell поддерживает высокую степень полиморфизма. В частности

readAny n stream = foldl next ([], stream) [1..n] 
    where 
    next (lst, x:xs) _ = (lst ++ [v], xs) 
     where 
     v = read x 

имеет тип

readAny :: (Enum b, Num b, Read a) => b -> [String] -> ([a], [String]) 

таким образом

readInts :: (Enum b, Num b, Read a) => [String] -> ([Int], [String]) 
readInts = readAny 

readFloats :: (Enum b, Num b, Read a) => [String] -> ([Float], [String]) 
readFloats = readAny 

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

Невозможно передать типы в качестве аргументов в Haskell. Редко вам нужно. Для тех немногих случаев, когда это необходимо, вы можете моделировать поведение, передавая значение с требуемым типом.

У Haskell есть «полиморфизм типа возвращаемого типа», поэтому вам действительно не стоит беспокоиться о «передаче типа» - вероятность того, что функции будут делать то, что вы хотите, не сообщив им.

+3

По иронии судьбы, * передача типа * находится в некотором зависимом от реализации образом, что происходит за кулисами, поэтому вопрос не так уж далек от отметки .... – Ingo

+1

Многие реализации не так много передают тип, как передают тип последовательного свидетеля для значения типа класса. Что-то вроде 'id' вообще не нужно знать тип своих аргументов (пока все одинаковые размеры) –

+1

Вот что я имел в виду с * зависимым от реализации способом *. Но * id *, не имея ограничений, ничего не пропускает, я думаю. Тип гарантирует, что id не собирается ничего делать с аргументом, который потребует больше информации, чем просто «это некоторая ценность», но это должно подразумеваться. – Ingo

9

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

readList' :: Read a => [String] -> [a] 
readList' = map read 


ints = readList' ["1", "2"] :: [Int] -- [1, 2] 

floats = readList' ["1.0", "2.0"] :: [Float] -- [1.0, 2.0] 

Чтобы прочитать только п вещь из потока, использовать take

Смежные вопросы