2011-02-07 39 views
10

Tôi đã nghiên cứu một số ngôn ngữ lập trình Haskell và bây giờ tôi phát hiện ra rằng có thể gọi hàm Haskell từ các chương trình C. Trong các nghiên cứu Haskell của tôi, tôi đã tạo ra một bộ đếm tần số từ với Haskell và tôi muốn thử gọi hàm đó từ một chương trình C, nhưng tôi không biết cách thực hiện nó. Tôi thấy hai trang web này trên haskell.org:Làm thế nào để chọn đúng loại Haskell C?

Calling Haskell from C

Foreign C types (Haskell module)

Mặc dù vậy, tôi là một chút mất mà loại để sử dụng. Chương trình haskell của tôi là một đường ống gồm các chức năng sau:

putStr. unlines. bản đồ testF. sortedTree

nơi các chức năng của riêng tôi

  • testF là loại testF :: Hiện a => ([Char], a) -> [Char]
  • sortedTree là loại được sắp xếpTree :: (Num a, Ord a) => [Char] -> [([Char], a)]

Tôi khá chắc chắn rằng tôi cần phải chuyển đổi các loại của từng chức năng thành loại C, thay vì chỉ chuyển đổi chức năng gọi đường ống. Các loại chức năng "chính" là

fileFreq :: [Char] -> IO()

Bên cạnh đó tất cả điều này, tôi đang sử dụng một cây nhị phân Haskell, đó không phải là loại khúc dạo đầu.

Dưới đây là toàn bộ mã Haskell:

module WordCounter where 

import List 
import Char 
import Foreign.C.Types 

data BTree a = Tip | BNode a (BTree a) (BTree a) deriving Show 

insertFreq x Tip = BNode (x,1) Tip Tip 
insertFreq x (BNode (q,p) l r) | (map toLower x)==(map toLower q) = BNode (q, p+1) l r 
       | otherwise     = BNode (q,p) l (insertFreq x r) 

tlist :: BTree a -> [a] 
tlist Tip = [] 
tlist (BNode x l r) = concat [tlist l, [x], tlist r] 

sortedTree x = sortBy (\(x,y) (p,q) -> compare q y) (tlist (foldr insertFreq Tip (words x))) 

testF (x, n) = concat (x : ":" : " \t\t\t " : show n : []) 

concord = putStr . unlines . map testF . sortedTree 

fileFreq filename = do { text <- readFile filename; concord text } 

bất cứ ai có thể hướng dẫn cho tôi một chút với điều này?

+5

mát câu hỏi, không may không ai trên ma trận này có vẻ là hữu ích http://asset.soup.io/asset/0750/2820_15d5_960.jpeg Tôi hy vọng nó không phải là xúc phạm, chỉ thú vị trong khi bạn chờ đợi một câu trả lời hợp lý. +1 tất nhiên – stacker

+0

Điều này không rõ ràng với tôi. Bạn có thể rõ ràng về hàm Haskell nào bạn muốn gọi từ C? Giả sử bạn có các ràng buộc làm việc - một người gọi C đơn giản trông như thế nào? –

+1

@stacker: Vô giá: D – Skurmedel

Trả lời

7

Điều bạn cần làm là tạo các hàm bao bọc cho các hàm bạn cần tiếp xúc với C và có công việc chuyển đổi từ loại C sang loại haskell.

Bạn cũng sẽ cần phải bật tiện ích mở rộng ForeignFunctionInterface, cũng bất kỳ ngoại lệ nào có thể xảy ra trong mã haskell cần phải được xử lý trong các hàm của trình bao bọc.

Ví dụ, nếu bạn chỉ cần để lộ top-level của bạn chức năng fileFreq C bạn có thể thêm một chức năng như:

fileFreq_hs :: CString -> IO CInt 
fileFreq_hs cstr = catch (wrap_fileFreq cstr) (\_ -> return (-1)) 
    where wrap_fileFreq = do 
      str <- peekCString cstr 
      fileFreq str 
      return 0 

để tạo ra một chức năng mà marshals một C-chuỗi thành một chuỗi Haskell (sử dụng hàm từ Foreign.C.String), gọi hàm fileFreq của bạn và dịch ngoại lệ thành mã lỗi C (-1 nếu ngoại lệ xảy ra, 0 ngược lại).

Sau đó, bạn cần phải xuất khẩu nó bằng cách sử

foreign export ccall fileFreq_hs :: CString -> IO CInt 

và tất nhiên bạn cần phải thêm:

{-# LANGUAGE ForeignFunctionInterface #-} 

ở phía trên cùng của mô-đun của bạn.

Sau đó, bạn có thể làm theo hướng dẫn trong các liên kết bạn đã cung cấp để biên dịch tệp này thành tệp C-stub và tiêu đề và tạo tệp C mà bạn có thể biên dịch bằng ghc.

Tất nhiên là có thể bọc bất kỳ chức năng nào bạn có, bạn chỉ cần đảm bảo xử lý các ngoại lệ có thể và so sánh giữa các loại C và loại haskell.

Các mã hoàn chỉnh với những thay đổi của tôi là:

{-# LANGUAGE ForeignFunctionInterface #-} 
module WordCounter where 

import List 
import Char 
import Foreign.C.Types 
import Foreign.C.String 
import Control.Monad 

data BTree a = Tip | BNode a (BTree a) (BTree a) deriving Show 

insertFreq x Tip = BNode (x,1) Tip Tip 
insertFreq x (BNode (q,p) l r) | (map toLower x)==(map toLower q) = BNode (q, p+1) l r 
       | otherwise     = BNode (q,p) l (insertFreq x r) 

tlist :: BTree a -> [a] 
tlist Tip = [] 
tlist (BNode x l r) = concat [tlist l, [x], tlist r] 

sortedTree :: (Ord t, Num t) => String -> [([Char], t)] 
sortedTree x = sortBy (\(x,y) (p,q) -> compare q y) (tlist (foldr insertFreq Tip (words x))) 

testF :: (Show t) => ([Char], t) -> [Char] 
testF (x, n) = concat (x : ":" : " \t\t\t " : show n : []) 

concord = putStr . unlines . map testF . sortedTree 

fileFreq filename = do { text <- readFile filename; concord text } 

fileFreq_hs :: CString -> IO CInt 
fileFreq_hs cstr = catch (wrap_fileFreq cstr) (\_ -> return (-1)) 
    where wrap_fileFreq cstr = do 
      str <- peekCString cstr 
      fileFreq str 
      return 0 
foreign export ccall fileFreq_hs :: CString -> IO CInt 
+0

Cảm ơn bạn đã trả lời tuyệt vời. – zaplec

+1

Tôi đã thử mã của bạn và tôi có thể biên dịch mã mà không bị lỗi. Tuy nhiên có ít nhất hai điều mà tôi đang tự hỏi: a) Tại sao chỉ có chức năng fileFreq được gọi là "chuyển đổi"? Trình biên dịch GHC có tự động xử lý các loại subfunction không? b) Tôi đã cố gắng hiểu tập tin nguồn _stub.c rất kỳ lạ và tiếc là tôi không biết làm cách nào để nhập tệp văn bản mà tôi muốn xử lý. Hàm definiton của hàm fileFreq trong tệp .c là "HsInt32 fileFreq_hs (HsPtr a1)". – zaplec

+1

Nơi duy nhất bạn cần lo lắng về các loại là các hàm bạn muốn xuất sang mã C. Ở đó và chỉ có bạn cần phải chuyển đổi giữa các loại C và các loại Haskell. Các loại subfunction không cần phải được sửa đổi, và thực sự không nên sửa đổi. Trình biên dịch GHC sẽ không làm bất cứ điều gì về các kiểu subfunction, thay vào đó bạn chuyển đổi một cách rõ ràng sang các kiểu haskell, sau đó tất cả các hàm khác sử dụng các kiểu Haskell. Là người dùng C, bạn chỉ cần bao gồm tệp tiêu đề được tạo và sau đó biên dịch tất cả các tệp cùng với tệp C của bạn bằng GHC (theo liên kết bạn có trong câu hỏi của mình). – dnaq

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