简体   繁体   中英

Can anyone explain me the meaning of read function in the following code in haskell

toDigits n = map (\c -> read [c]) (show n)

我无法理解上述代码中的读取功能的含义

Note : all toDigits here only work with positive numbers (zero included), and will error on negative numbers.

read has signature:

read :: Read a => String -> a

So based on the signature we can say that if a type a is Read a , then it turns a String into that a . In natural language it means that it parses a textual representation of an object into that object. For instance it can parse "\\"foo\\"" into the String "foo" . Or it can parse "23" into the integer 23 . It is the output type that determines into what we parse the elements, but based on the function, it has probably some type signature toDigits :: Int -> Int .

Nowe we still have to understand how the function works. show :: Show a => a -> String is actually the opposite of read : it converts an object into a textual representation. So 23 is for instance converted into "23" , a String .

So first we convert a number, for instance 1425 into "1425" . A String is a list of Char s, or more formal type String = [Char] . So that means we can perform a map on it, and map will thus process each Char of the String , one at a time.

Now for each char c , we first construct a list [c] . Why do we do that? To make it a String . Since a String is a list of chars. A list with one character is equal to a string with that character. Next we call read on the 1-char String, to turn it into whatever we like.

This means that toDigits has as most generic type:

toDigits :: (Show a, Read b) => a -> [b]

Note that according to this signature, we are not bounded by the fact that a should be a number, nor that the output should be numbers. As long as the textual representation of the a object can be split into 1-char strings that can be parsed indivudually into object of b , we are fine. However in reality, it will probably only make sense if both a and b are Integral , so:

toDigits :: (Show a, Read b, Integral a, Integral b) => a -> [b]

A final note is that we can make the implementation a bit more elegant, like:

toDigits :: (Show a, Read b, Integral a, Integral b) => a -> [b]
toDigits = map (read . pure) . show

We can further restrict the output type to Int , since an Int is guaranteed to have all numbers between (and including) 0 and 9, and construct:

import Data.Char(digitToInt)

toDigits :: (Show a, Integral a) => a -> [Int]
toDigits = map digitToInt . show

That being said, the function makes some assumptions: for instance that all numbers will have a textual representation that is the decimal notation. This is not per se guaranteed: I could construct my own number system, and decide to construct a show that for instance uses the unary system (although that is probably not a good idea). This makes it a bit a dangerous approach: you rely on some assumptions that are not guaranteed. Furthermore this

We can use the div and mod to calculate the digits. For instance like:

toDigits :: Integral a => a -> [a]
toDigits 0 = [0]
toDigits n | n < 0 = error "Negative number!"
           | otherwise = step [] n
    where step xs 0 = xs
          step xs d = step ((mod d 10):xs) (div d 10)

furthermore we dropped the Show a type constraint, so integrals that do not have such show can still be handled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM