Я сделал функцию, похожую на numpy's array
. Он преобразует списки в массивы, списки списков для 2d массивов и т.д.Типы типов Haskell и фиктивные аргументы
Это работает так:
ghci> arrFromNestedLists ["hello", "world"] :: Array (Int, (Int,())) Char
array ((0,(0,())),(1,(4,()))) [((0,(0,())),'h'),((0,(1,())),'e'),((0,(2,())),'l'),((0,(3,())),'l'),((0,(4,())),'o'),((1,(0,())),'w'),((1,(1,())),'o'),((1,(2,())),'r'),((1,(3,())),'l'),((1,(4,())),'d')]
(Int, (Int,()))
и не (Int, Int)
, потому что я не знаю о programatic способ увеличить длину кортеж. (вопрос стороны: есть ли такой способ?)
Кодирование этого было неудобным, и мне пришлось сделать «обходной путь» (проходящий вокруг фиктивных аргументов функций), чтобы он работал. Интересно, есть ли лучший способ.
Так вот код, прерываемый с подробной информацией о некрасивых обходные:
{-# LANGUAGE FlexibleInstances, ScopedTypeVariables, TypeFamilies #-}
type family ListOfIndex i a
type instance ListOfIndex() a = a
type instance ListOfIndex (Int, i) a = [ListOfIndex i a]
class Ix i => ArrConv i where
acBounds :: a -> ListOfIndex i a -> (i, i)
acFlatten :: i -> ListOfIndex i a -> [a]
acBounds
"должен" быть :: ListOfIndex i a -> (i, i)
. И аналогично для acFlatten
. Каждому дается фиктивная переменная (undefined
всегда значение дано), потому что в противном случае я не мог заставить его компилировать :(
arrFromNestedLists :: forall i a. ArrConv i => ListOfIndex i a -> Array i a
arrFromNestedLists lst =
listArray
(acBounds (undefined :: a) lst)
(acFlatten (undefined :: i) lst)
Выше фиктивная undefined
аргумент прохождения на работе. Он рассказывает GHC, какой экземпляр ListOfIndex
использовать.
instance ArrConv() where
acBounds _ = const ((),())
acFlatten _ = (: [])
ниже функция должна была acBounds
функции в экземпляре ArrConv
и объявлена вне только потому, что мне нужно использовать ScopedTypeVariables
, и я не знаю, как я могу сделать это в функции в определении экземпляра ..
acSucBounds
:: forall a i. ArrConv i
=> a -> [ListOfIndex i a] -> ((Int, i), (Int, i))
acSucBounds _ lst =
((0, inStart), (length lst - 1, inEnd))
where
(inStart, inEnd) = acBounds (undefined :: a) (head lst)
instance ArrConv i => ArrConv (Int, i) where
acBounds = acSucBounds
acFlatten _ = concatMap (acFlatten (undefined :: i))
«Я не знаю программного способа увеличить длину кортежа». Я не думаю, что ты можешь. Это прекрасный пример функции, тип которой зависит от значения. Это было бы легко сделать на навязчивом языке, таком как «Агда», но невозможным в Haskell. Может быть, вы могли бы каким-то образом использовать «GADTs», чтобы дать вам какое-то зависимое поведение, но с моей точки зрения, я не знаю, как это сделать. –
Может быть, Шаблон Haskell может быть полезен: http://www.haskell.org/bz/thdoc.htm http://www.haskell.org/haskellwiki/Template_Haskell – primodemus
@primodemus: С TH я могу сделать экземпляры для 'ArrConv' для массивов до 10 измерений, и они будут использовать обычные кортежи для индексов, что является улучшением.Но я бы чувствовал, что предел произволен, и код, вероятно, будет гораздо менее удобочитаемым. – yairchu