2010-01-11 41 views
5

Tôi đã thực hiện một chức năng tương tự như array của numpy. Nó chuyển đổi danh sách này để các mảng, danh sách của danh sách này để mảng 2ngày vvGia đình loại Haskell và đối số giả

Nó hoạt động như thế này:

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,())) và không (Int, Int) bởi vì tôi không biết một cách programatic để tăng chiều dài của một tuple. (câu hỏi phụ: có cách nào không?)

Mã hóa của nó là khó xử và tôi phải thực hiện "giải pháp thay thế" (chuyển qua các đối số giả cho hàm) để nó hoạt động. Tôi tự hỏi nếu có một cách tốt hơn.

Vì vậy, đây là mã, bị gián đoạn với các chi tiết của các biện pháp khắc phục xấu xí:

{-# 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 "nên" được :: ListOfIndex i a -> (i, i). Và tương tự cho acFlatten. Mỗi được đưa ra một biến giả (undefined luôn là giá trị nhất định) vì nếu không tôi không thể có được nó để biên dịch :(

arrFromNestedLists :: forall i a. ArrConv i => ListOfIndex i a -> Array i a 
arrFromNestedLists lst = 
    listArray 
    (acBounds (undefined :: a) lst) 
    (acFlatten (undefined :: i) lst) 

Trên đây là hình nộm undefined luận qua tại nơi làm việc. Nó kể về GHC mà thể hiện của ListOfIndex để sử dụng.

instance ArrConv() where 
    acBounds _ = const ((),()) 
    acFlatten _ = (: []) 

hàm dưới đây cần phải có được sự acBounds chức năng trong một thể hiện của ArrConv, và được khai báo bên ngoài chỉ vì tôi cần phải sử dụng ScopedTypeVariables và tôi không biết làm thế nào tôi có thể làm điều đó trong một trong một định nghĩa cá thể ..

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)) 
+3

"Tôi không biết cách có lập trình để tăng độ dài của bộ túp". Tôi không nghĩ bạn có thể. Đó là một ví dụ hoàn hảo về một hàm có kiểu phụ thuộc vào một giá trị. Nó sẽ được dễ dàng để làm trong một ngôn ngữ phụ thuộc gõ như 'Agda', nhưng không thể trong Haskell. Có lẽ bạn có thể sử dụng 'GADTs' theo cách nào đó để cho bạn một số hành vi phụ thuộc, nhưng ngoài đỉnh đầu của tôi, tôi không biết làm thế nào. –

+0

Có thể Mẫu Haskell có thể hữu ích: http://www.haskell.org/bz/thdoc.htm http://www.haskell.org/haskellwiki/Template_Haskell – primodemus

+0

@primodemus: Với TH tôi có thể tạo phiên bản cho 'ArrConv' cho các mảng có tối đa 10 thứ nguyên và chúng sẽ sử dụng các bộ dữ liệu bình thường cho các chỉ mục, đó là một sự cải tiến.Nhưng tôi sẽ cảm thấy giới hạn là tùy ý và mã có lẽ sẽ ít dễ đọc hơn. – yairchu

Trả lời

4

Lý do mà những lập luận thêm để acBounds và acFlatten là cần thiết là các loại ai không thể phục hồi từ ListOfIndex i a -> (i, i)ListOfIndex i a -> [a] tương ứng. Một cách giải quyết khác là kết hợp hai phương thức thành một phương thức acArgs loại ListOfIndex i a -> ((i, i), a). Bây giờ vấn đề duy nhất là sử dụng nó trong trường hợp của (Int, i) theo cách ngăn cản người đánh máy từ việc khái quát hóa loại của nó quá nhiều gây ra cùng một vấn đề như trước (ví dụ, chúng ta không thể đơn giản sử dụng fst . acArgs).

 
{-# LANGUAGE TypeFamilies, FlexibleInstances #-} 

import Data.Array 

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 
    acArgs :: ListOfIndex i a -> ((i, i), [a]) 

instance ArrConv() where 
    acArgs x = (((),()), [x]) 

instance ArrConv i => ArrConv (Int, i) where 
    acArgs lst = 
    (((0, inStart), (length lst - 1, inEnd)), args >>= snd) 
    where 
     args = map acArgs lst 
     (inStart, inEnd) = fst (head args) 

arrFromNestedLists :: ArrConv i => ListOfIndex i a -> Array i a 
arrFromNestedLists = uncurry listArray . acArgs 
+0

@Reid Barton: Awesome! Cảm ơn :) Tôi refactored câu trả lời của bạn một chút (Tôi hy vọng bạn không nhớ): Thay vì đi 'acArgs' với một hàm làm cho nó đơn hình, nó bây giờ chỉ sử dụng nó ở một nơi. – yairchu

+0

Ah, tôi hiểu rồi. Tốt đẹp. –

0

Nếu bạn muốn giữ acBoundsacFlatten riêng biệt, bạn có thể thêm một type-level tag argument với nó, ví dụ: acBounds sẽ có loại acBounds :: Proxy a -> ListOfIndex i a -> (i, i). Điều này loại bỏ sự cần thiết cho các đối số undefined, vì bạn chỉ có thể vượt qua (Proxy :: SomeConcreteType) đối số đó; và acBounds không có cách nào để trích xuất bất kỳ thông tin mức giá trị hữu ích nào từ nó, vì nó là đẳng cấu (theo cách không được phân loại) cho loại đơn vị.

Các vấn đề liên quan