Reputation: 41
how to solve identity for a matrix using Haskell scripts? For example, if with this given type
type Matrice a = [[a]]
identity :: Int -> Maybe (Matrice Int)
How can it return the identity matrice for the given size? I know that identity matrice is a square matrice which has zero for all values, except the values on the top-left to bottom-right diagonal which are all one. With the condition of, if the size is less than 1, then the identity matrice isn't defined and Nothing is returned.
So say for example,
Prelude > identity 5
Just [[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]]
Prelude > identity 2
Just [[1,0],[0,1]]
I've tried
identity1 :: Int -> Int -> [Int]
identity1 a b
| a == 0 []
| b == 0 (1:identity (a-1) (-1))
| otherwise = (0:identity' (a-1) (b-1))
identity2 :: Int -> Int -> Matrice Int
identity2 a b
| b == 0 []
| otherwise = (0:identity1 (a-1) (b-1) : identity2 a (b-1)
Upvotes: 2
Views: 876
Reputation: 531888
One short approach is to define the "infinite" identity matrix as
ii = (1 : repeat 0) : fmap (0:) ii
The first row is 1 0 0 ...
; each subsequent row is the row above it with a 0 prepended to it.
It should be obvious that the first n rows of the first n columns of the infinite identity matrix is In.
1 | 0 | 0 | 0 | 0 | 0 |
--+ | | | | |
0 1 | 0 | 0 | 0 | 0 |
------+ | | | |
0 0 1 | 0 | 0 | 0 |
----------+ | | | ...
0 0 0 1 | 0 | 0 |
--------------+ | |
0 0 0 0 1 | 0 |
------------------+ |
0 0 0 0 0 1 |
----------------------+
. .
. .
. .
Given that, we just use take
to obtain the appropriate-sized sub matrix. take n
applied to each row will return the first n
columns, and take n
applied to the result takes just the first n
rows.
type Matrix a = [[a]]
identity :: Int -> Maybe (Matrix Int)
identity n | n <= 0 = Nothing
| otherwise = let ii = (1:repeat 0) : (fmap (0:) ii)
in Just $ take n (take n <$> ii)
If recursively defined infinite lists tie your brain in knots, you can also just define an enlarge
function that generates In+1 from In. To do so, it is convenient to assume that I0 exists and is represented as an empty list.
enlarge :: Matrix Int -> Matrix Int
enlarge [] = [[1]]
enlarge i@(r:_) = (1:(0<$r)) : fmap (0:) i
Then you can define identity :: Int -> Matrix Int
by indexing an infinite list of identity matrices
identity n | n <= 0 = Nothing
identity n = Just (identities !! n)
where identities :: [Matrix Int]
is built with either iterate
identities = iterate enlarge []
or Data.List.unfoldr
:
identities = unfoldr (\x -> Just (x, enlarge x)) []
It's also worth noting that the infinite identity matrix is the fixed point of enlarge
:
import Data.Function
ii = fix enlarge
Upvotes: 6
Reputation: 67507
deconstructivism
identity n = splitEvery n $ (concat $ replicate (n-1) $ 1: replicate n 0)++[1]
proof without words
[[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]] ~
[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1] ~
[1,0,0,0,0,0],[1,0,0,0,0,0],[1,0,0,0,0,0],[1,0,0,0,0,0] ++ [1] ~
[1,0,0,0,0,0]{4} ++ [1] ~
(1:[0]{5}){4} ++ [1]
you need to handle special cases (n<0
and n==1
)
Upvotes: 1
Reputation: 477210
In case one needs some iterative approach in Haskell, recursion is used. This means that we need to define base case(s) as well as inductive case(s).
There are two base cases here:
Nothing
;Just
with a 1×1 matrix:1
There is one inductive case: in case the number is greater than 1
, we first generate the identity matrix for n-1
, and then we add row at the top and a column at the left:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
this means we thus need to prepend all rows of the previous matrix with 0
, and we prepend the matrix with a list that contains one 1
and n-1
zeros.
Let us first forget about the first base case (n
is less than or equal to zero), and assume n
is always strictly positive. In that case there is no need to wrap the value in a Maybe
, so we first construct a function:
identity' :: Int -> Matrix Int
identity' = ...
so the base case is where the parameter is 1
:
identity' 1 = ...
and the inductive case has shape:
identity' n = first_row : map prepend_zero (identity (n-1))
where first_row = ...
prepend_zero = ...
Now we can construct identity
in terms of identity'
by only once check whether the value is less than or equal to zero:
identity :: Int -> Maybe (Matrix Int)
identity n | n <= 0 = Nothing
| otherwise = Just (identity' n)
where identity' 1 = ...
identity' n = first_row : map prepend_zero (identity (n-1))
where first_row = ...
prepend_zero = ...
I leave the expressions (...
) as an exercise that should probably be reasonable.
Upvotes: 2
Reputation: 89093
One way to accomplish this is through recursion.
I'm going to ask you some leading questions, since I haven't seen what you've tried so far:
Or in pseudo-code:
identity 1 = Just $ _1x1_identity
-- _1x1_identity :: [[Int]]
identity n_plus_1 | n_plus_1 > 1 = fmap _alter_identity (identity n)
where n = n_plus_1 - 1
-- _alter_identity :: [[Int]] -> [[Int]]
identity n | n < 1 = Nothing
If you're unfamiliar with fmap
, it's used here to unwrap/rewrap the Maybe
value returned from the other call to identity
.
I could do the same more verbosely as
identity n_plus_1 | n_plus_1 > 1 = case identity n of
Nothing -> Nothing
Just matrix -> Just (_alter_identity matrix)
where n = n_plus_1 - 1
Your approach in the comments attempts to construct the entire matrix row by row, which is also workable.
One way to implement that approach more directly is through a list comprehension.
List comprehensions make it easy to define new lists and lists of lists:
Prelude> [ i | i <- [0..4] ]
[0,1,2,3,4]
Prelude> [ [(i,j) | j <- [0..3]] | i <- [0..2] ]
[ [(0,0),(0,1),(0,2),(0,3)]
, [(1,0),(1,1),(1,2),(1,3)]
, [(2,0),(2,1),(2,2),(2,3)]
]
Above we can see that we can use a list comprehension to generate a matrix of coordinates - the value (i,j)
shows up in the i
'th row and the j
th column.
List comprehensions allow you to place arbitrary expressions on the left-hand-side of the |
, so I could do i + j
instead of (i,j)
to get a very different matrix:
Prelude> [ [i + j | j <- [0..3]] | i <- [0..2] ]
[ [0,1,2,3]
, [1,2,3,4]
, [2,3,4,5]
]
That's a rectangular matrix. A square matrix would use the same bounds for i
and j
.
If you were to use a list comprehension like that to create a square matrix, what expression would you put to the left hand side of the |
to create the identity matrix? To put it another way, can you express the identity matrix's value at row i
column j
in terms of i
and j
?
Upvotes: 4