В това упражнение трябва да напиша функция, коятополучава списък на цели числа като аргумент и дава матрица или списък със списъци. Въпросът в правенето на матрицата е, че целите числа представляват числото True
във всяка колона на матрицата. Например
[2,4,1]
трябва да бъде преведен на:
който в системата е представен като списък със списъци:
[ [0,1,0], [0,1,0], [1,1,0], [1,1,1] ]
Тъй като не е лесно да се манипулират матрици (списък със списъци) по колони, използвах трик и завъртях матрицата на 90 градуса вляво, използвайки transpose
поради което матрицата изглежда по-долу:
Тогава разработих следния алгоритъм за решаване на проблема:
- Вземете първия елемент от списъка за въвеждане
- Създайте списък с дължина
maximum xs
(дължината на всеки списък е равна на максималния елемент в списъка) - Сложете толкова много
True
в списъка, както определя първият елемент. - Попълнете останалата част от списъка с
False
- Направете същото за всички елементи и завъртете матрицата
Опитах да внедря две решения, но всяко от тях има проблем, който не мога да разреша:
Този работи добре за първия елемент, но не знам как да го прилагам към всички елементи от списъка за въвеждане
listToMatrix x = (replicate ((maximum x) - (head x)) False) ++ (replicate (head x) True)`
Това работи за всички елементи, но не може да запази дължината на вътрешния списък, така че списъците да имат различна дължина.
listToMatrix lst@(x:xs) = ((replicate ((maximum lst) - x) False) ++ (replicate x True)) : listToMatrix xs`
Въпрос 1: Как мога да накарам тези функции да работят с минимални промени?
Въпрос 2: Има ли по-елегантни и компактни решения?
Послепис Използвах 1 и 0 в матриците, за да ги направя по-четими, но в действителност са верни и неверни
Отговори:
3 за отговор № 1Използвах следния подход, който е съвместим с вашия.
Както предложихте, ние използваме transpose
в края, тъй като транспонираната матрица изглежда по-лесна за генериране.
f :: [Int] -> [[Bool]]
f xs = transpose (...)
След това, всеки елемент на xs
трябва да генерира нов ред. Можем да използваме разбиране на списъка (направено по-долу) или да използваме алтернативно map
.
f :: [Int] -> [[Bool]]
f xs = transpose [ row x | x <- xs ]
where row :: Int -> [Bool]
row x = ...
Както предлагате, ние също имаме нужда maximum
за генериране на всеки ред, така че го изчисляваме веднъж:
f :: [Int] -> [[Bool]]
f xs = transpose [ row x | x <- xs ]
where m = maximum xs
row :: Int -> [Bool]
row x = ... -- we know x and m, we need m-x Falses and x Trues
Сега просто трябва да адаптирате кода си.
3 за отговор № 2
Ето как ще го направя:
toMatrix" :: [[Bool]] -> [Int] -> [[Bool]]
toMatrix" acc xs
| or bools = toMatrix" (bools : acc) (map pred xs)
| otherwise = acc
where bools = map (> 0) xs
toMatrix :: [Int] -> [[Bool]]
toMatrix = toMatrix" []
Просто и ясно. Няма нужда да се транспонира.
Ето визуализацията на моята програма. Ще използвам 0
и 1
за False
и True
съответно.
toMatrix [2,4,1] = toMatrix" [] [ 2, 4, 1]
= toMatrix" [[1,1,1]] [ 1, 3, 0]
= toMatrix" [[1,1,0],[1,1,1]] [ 0, 2,-1]
= toMatrix" [[0,1,0],[1,1,0],[1,1,1]] [-1, 1,-2]
= toMatrix" [[0,1,0],[0,1,0],[1,1,0],[1,1,1]] [-2, 0,-3]
= [[0,1,0],
[0,1,0],
[1,1,0],
[1,1,1]]
Когато се обаждаме toMatrix" acc xs
първо изчисляваме bools = map (> 0) xs
, Ако всички елементи на bools
сте False
тогава сме готови. Ние просто се връщаме acc
, В противен случай добавяме bools
до началото на acc
и извадете по един от всеки елемент на xs
, Изплакнете и повторете.
Няма нужда да следите най-големия елемент от xs
и няма нужда да транспонирате матрицата. Simple.