在Haskell中将指针传递给C函数的数组

4

我有以下的C代码:

#include <sys/times.h>
#include <time.h>


float etime_( float *tarray )
{   struct tms buf;

    times( &buf );

    tarray[0] = 1.0 * buf.tms_utime / CLOCKS_PER_SEC;
    tarray[1] = 1.0 * buf.tms_stime / CLOCKS_PER_SEC;

    return tarray[0] + tarray[1];
}

正在尝试将此Fortran代码移植到Haskell:

  PROGRAM Test
    IMPLICIT NONE

    REAL t, ta(2), etime

    INTEGER i
    DOUBLE PRECISION x

    do i = 1, 10000
       x = sin( cos( i * 1.0 d0 ) )
       print *, x 
    enddo

    ta(1) = 0.0d0
    ta(2) = 0.0d0

    t = etime( ta )       

    PRINT *, 'user time:    ', ta(1)
    PRINT *, 'system time:  ', ta(2)
    PRINT *, 'process time: ', t

  END

如何定义数组和 ! 或 !!! 才能使下面的代码工作?
module Main where

import GHC.Ptr
import GHC.Prim
import System.IO.Unsafe
import Control.Monad

foreign import ccall etime_ :: Ptr DoubleIO Double
etime = etime_

main :: IO Int
main = do
  mapM_ (print . sin . cos . (* (1.0 :: Double)) . fromIntegral) [1..10000 :: Int]
  ta ← array 2
  t ← etime ta
  putStrLn $ "user time:    " ++ show (ta !!! 0)
  putStrLn $ "system time:  " ++ show (ta !!! 1)
  putStrLn $ "process time: " ++ show t
  return 0

array :: IntIO (Ptr a)
array size = undefined

(!) :: Ptr a → IntIO a
(!) = undefined

(!!!) :: Ptr a → Int → a
(!!!) = undefined
3个回答

4
我建议这样做:
{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign.Marshal.Array (allocaArray, peekArray)
import Foreign.Ptr (Ptr)

foreign import ccall etime_ :: Ptr Float -> IO Float

main :: IO ()
main = do
  mapM_ (print . sin . cos . (* (1.0 :: Double)) . fromIntegral) [1..10000 :: Int]
  allocaArray 2 $ \ta -> do
    ptime <- etime_ ta
    [utime, stime] <- peekArray 2 ta
    putStrLn $ "user time:    " ++ show utime
    putStrLn $ "system time:  " ++ show stime
    putStrLn $ "process time: " ++ show ptime

但是要回答你的具体问题:

array :: Storable a => Int -> IO (Ptr a)
array = mallocArray

(!) :: Storable a => Ptr a -> Int -> IO a
(!) = peekElemOff

(!!!) :: Storable a => Ptr a -> Int -> a
(!!!) ptr = unsafePerformIO . peekElemOff ptr

我建议使用Hoogle来查找你需要的函数。


1
返回总和、用户时间和系统时间的元组。请注意,etime需要一个指向float而不是double的指针,因此请确保您的类型相符:
{-# LANGUAGE ForeignFunctionInterface #-}

module Main where

import Foreign (Ptr)
import Foreign.Marshal.Array (allocaArray,peekArray)
import Control.Monad (mapM_)

foreign import ccall etime_ :: Ptr Float -> IO Float

etime :: IO (Float, Float, Float)
etime = do
  allocaArray 2 $ \ta -> do
    t <- etime_ ta
    [user,sys] <- peekArray 2 ta
    return (t,user,sys)

main :: IO Int
main = do
  mapM_ (print . sin . cos . (* (1.0 :: Double)) . fromIntegral) [1..10000 :: Int]
  (t,user,sys) <- etime
  putStrLn $ "user time:    " ++ show user
  putStrLn $ "system time:  " ++ show sys
  putStrLn $ "process time: " ++ show t
  return 0

在Haskell中,浮点数和双精度之间有精度差别吗?它们都是原语吗?还是说浮点数到双精度就像整数到int一样? - Cetin Sert
是的:Prelude分别为单精度浮点数和双精度浮点数定义了Float和Double。请参阅Haskell报告中的Numbers部分:http://www.haskell.org/onlinereport/basic.html#sect6.4 - Greg Bacon

-1
给定以下额外的 Haskell 模块:
module AllocSymbols (

  (↑≣), -- mallocArray
  (≣⊠), -- free

  (≣→), -- peekElemOff
  (≣←), -- pokeElemOff

  (⥱), -- peekElemOff unsafe

  (⥅), -- advancePtr

) where

import GHC.Ptr
import System.IO.Unsafe
import Foreign.Storable
import Foreign.Marshal.Alloc (free)
import Foreign.Marshal.Array

(↑≣) :: Storable a ⇒ IntIO (Ptr a)
(↑≣) = mallocArray

(⥱) :: Storable a ⇒ Ptr a → Int → a
a ⥱ i = unsafePerformIO $ peekElemOff a i

(≣→) :: Storable a ⇒ Ptr a → IntIO a
(≣→) = peekElemOff

(≣←) :: Storable a ⇒ Ptr a → Int → a → IO ()
(≣←) = pokeElemOff

(⥅) :: Storable a ⇒ Ptr a → IntPtr a
(⥅) = advancePtr

(≣⊠) :: Ptr a → IO ()
(≣⊠) = Foreign.Marshal.Alloc.free

现在我们可以移植这段Fortran代码:

  PROGRAM Test
    IMPLICIT NONE

    REAL t, ta(2), etime

    INTEGER i
    DOUBLE PRECISION x

    do i = 1, 10000
       x = sin( cos( i * 1.0 d0 ) )
       print *, x 
    enddo

    ta(1) = 0.0d0
    ta(2) = 0.0d0

    t = etime( ta )       

    PRINT *, 'user time:    ', ta(1)
    PRINT *, 'system time:  ', ta(2)
    PRINT *, 'process time: ', t

  END

逐字逐句转换成 Haskell:

module Main where

import GHC.Ptr
import AllocSymbols
import Control.Monad

foreign import ccall etime_ :: Ptr FloatIO Float

main :: IO ()
main = do
  mapM_ (print . sin . cos . (* (1.0 :: Double)) . fromIntegral) [1 .. 10000 :: Int]

  ta ← (2 ↑≣) -- ta :: Ptr Float

  (ta ≣← 0) 0.0
  (ta ≣← 1) 0.0

  t ← etime_ ta

  putStrLn $ " user time:    " ++ show (ta ⥱ 0)
  putStrLn $ " system time:  " ++ show (ta ⥱ 1)
  putStrLn $ " process time: " ++ show t

  (ta ≣⊠) -- optional in this case

1
你这么喜欢运算符吗?我认为使用标准函数名称会使代码更适合新手。 - yairchu
我觉得自己有点奇怪和古怪 o__O。我非常喜欢它们~~~!!因为我的 Haskell 代码主要是给自己用的,所以我会自由地使用它们 ^o^!!但如果我有机会与他人合作完成一个项目,我一定会确保保持友好和可维护性,并避免引入太多间接性。 - Cetin Sert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接