23 сентября 2011 г.

Haskell. Начало программирования. Матрицы.

Небольшая предыстория о том, как начал программировать на Haskell-е
Среда
После встречи piter.rb#4 (в позапрошлую среду) познакомился через докладчиков с языком Haskell. Язык показался интересным для изучения и непонятным, с точки зрения применения. Т. е. нахрена он нужен.
Четверг
Почитал на wikipedia, на паре других ресурсах... меня терзало
Пятница
Невыдержал. Установил компилятор GHC. Накачал книжек в pdf формате. И покодил (вместо телевизара и прсомотра фильмов с торрентов). Да, я отказался от просмотра последних филтьмов с торрентов и просмотра передач с канала Discavery в пользу программирования на Haskell. На Haskell - как основной язык программирования не подсел - так чисто любительское.
Суббота
Меня осенило. Haskell чудокават, но в полне по силам его освоить. Ребята (Дронов к примеру) не так позиционирует язык. Многие термины используемые в программировании Haskell не являются чем-то не знакомым обычным (особенно после внятной расшифровки) программистам императивщикам.
Сегодня
Неделю спустя, все еще программирую на запале интереса...

Объективные "-" компилятора ghc (так как он единственный для языка то и языка )
  1. Довольно объемный исполняемый файл ~ 1 Mb,
  2. Быстрой работы пока не заметил.
Самокритика объективных "-"
  • А нужна ли эффективность-быстродействие программы?
Для практики написал программу с реализацией на Haskell алгоритма подсчета определителя матрицы, перемножения двух матриц. Функцию транспонирования матриц на Haskell позаимствовал в рунете:

main=do
	print "Matrix detrminant:"
	print (matrixDet [[1,2,3],[7,5,6],[7,8,9]])
	print (matrixDet [[1,2],[4,5]])
	print (matrixDet [[1]])
	print (matrixDet [[]])
	print $ matrixDet [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
	print $ matrixDet [[7,4,6,0],[3,1,2,4],[9,7,2,3],[1,5,8,2]]
	print $ matrixDet [[(-7.9),4.1,6.7,0],[3,1,2,4],[9,7,2.3,3],[1,5,8,2]]
	print "Get row & column:"
	print $ getRow [[1,2,3],[4,5,6],[7,8,9]] 1 
	print $ getColumn [[1,2.9,3.0],[4,5.7,6],[7,8,9]] 1
	print "Multiplication of lists:"
	print $ sum $ map (\(x,y) -> x*y) (zip [1,2,3] [3,0,6])
	print $ sum $ zipWith (*) [1,2,3] [3,0,6]
	print $ sum $ zipWith (*) [1,2,3] [3,0]
	print "Matrix transponation:"
	print $ matrixTransp [[1,2,3],[1,2,3]]
	print $ transp [[1,2,3],[1,2,3]]
	print "matrix mult:"
	print $ matrMult [[1,2,3],[7,9,8]] [[8,7],[5,4],[1,0]]
	print $ matrMult [[1,2,3],[7,9,8]] [[5],[6],[7]]
	print $ matrixDet $ matrMult [[1,2,3],[7,9,8]] [[8,7],[5,4],[1,0]]
 
-- Функция подсчета определителя матрицы
matrixDet [[]] = 0
matrixDet [[x]] = x
matrixDet [[x1, x2], [x3, x4]] = x1*x4 - x2*x3
matrixDet m = let row = head m in
	sum (indexMap (\(x,i)->x*(znakFunc i)*(matrixDet (minor m 0 i))) row)
	where
	znakFunc x = if x`mod`2==0 then 1 else (-1) -- (-1)^(i+j), i=0, j=0.. 
-- перебор списка с индексом, \(x,i) -> ... , but i is Enum
	indexMap _func _list = map _func (zip _list [0..])
-- фильтрация списка с учетом индекса элемента в списке:
	indexFilter _func _array = map (\x -> fst x) (filter _func (zip _array [0..])) 
-- функция определения матрицы минора
	minor :: (Num a, Integral b) =>[[a]] -> b ->b -> [[a]] 
	minor matrix _i _j = map (\row -> indexFilter (\(_,j)->not(j==_j)) row) 
		(indexFilter (\(_,i)-> not(i==_i) ) matrix)
 
-- примитив: функция для извлечения строки
getRow :: (Integral b, Num a) => [[a]] -> b -> [a]
getRow m i = (m!!(fromEnum i))
 
-- примитив: функция для извлечения вектора
getColumn :: (Integral b, Num a) => [[a]] -> b -> [a]
getColumn m j = map (\row -> row!!(fromEnum j) ) m
 
-- 1-й вариант транспонирования матрицы:
matrixTransp :: [[a]] -> [[a]]
matrixTransp m = [[m!!i!!j| i<-[0..((length m)-1)]] |j<-[0..(length(m!!0)-1)]]
-- 2-й вариант транспонирования матрицы. Определение транспонированной матрицы через рекурсию:
-- транспонированная матрица - это такая матрица, первая строка которой равна
-- первому столбцу исходной, а остаток равен траспонированному остатку исходной
transp ([]:_) = []
transp m = map head m: transp (map tail m)
 
-- Функция перемножения двух матриц
matrMult :: (Num m)=> [[m]]->[[m]]->[[m]]
matrMult m1 m2= let _m2 = (transp m2) in 
	map (\r1-> (map (\c2-> sum(zipWith(*) r1 c2)) _m2 )) m1
-- Здесь чтобы во второй матрице не извлекать вектор (строку у добнее) 
-- вторую матрицу просто транспонируем в _m2