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.