はじめに
inline-cC 数値ライブラリを;でラップしています。一部の関数はステップ ルーチンにコールバックを渡すことができます。ODE の最適化または時間積分を考えてみてください。
特にネイティブ C の使用では、コールバックは連続した配列を操作し、ポインターを介してそれらを変更し、不透明な (分散された) データ構造にそれらを返すことができます。
したがって、これは変更可能なデータの問題であり、Haskell 側で表現したいと思います。私の理解では、コールバックで配列をフリーズし、たとえば aData.Vector.Storable.Vectorまたは withとして作業しrepa、結果を解凍し、外部ポインタを取得する必要があります。そしてそれを返します。
internals: newtype Vec = Vec (Ptr Vec) deriving Storable、およびinline-cContext 内の関連するエントリは、不透明な C データ構造へのポインタの型を表し、vecGetArray/vecRestoreArray隣接メモリへの同じポインタを生成/要求し、 を要求/生成しVecます。
Q:
Vector返されたものは正しいのですがVec、この関数から戻った後に変更された結果 (「副作用」) を使用すると、変更されていないことに気付きました。GHC はそれを再計算しません (怠惰?)。再計算するにはどうすればよいですか?FFI全体で変更可能なデータを操作するために、Haskellにはどのような慣用的な方法がありますか?
* 修理済み *
答えを見る
ありがとう!
import qualified Data.Vector.Storable as V
import qualified Data.Vector.Storable.Mutable as VM
withVecGetVectorM ::
Vec ->
(V.Vector PetscScalar_ -> IO (V.Vector PetscScalar_)) ->
IO (V.Vector PetscScalar_)
withVecGetVectorM v f = do
p <- vecGetArrayPtr v
pf <- newForeignPtr_ p
vImm <- V.freeze (VM.unsafeFromForeignPtr0 pf len)
vImmOut <- f vImm
vMutOut <- V.thaw vImmOut
let (fpOut, _, _) = VM.unsafeToForeignPtr vMutOut
pOut = unsafeForeignPtrToPtr fpOut
vecRestoreArrayPtr v pOut
return vImmOut
where len = vecSize v
Vec.hs :
vecGetArrayPtr :: Vec -> IO (Ptr PetscScalar_)
vecGetArrayPtr v = chk1 (vecGetArrayPtr' v)
vecRestoreArrayPtr :: Vec -> Ptr PetscScalar_ -> IO ()
vecRestoreArrayPtr v ar = chk0 (vecRestoreArrayPtr' v ar)
InlineC.hs
-- PETSC_EXTERN PetscErrorCode VecGetArray(Vec,PetscScalar**);
vecGetArrayPtr' :: Vec -> IO (Ptr PetscScalar_, CInt)
vecGetArrayPtr' v = withPtr $ \p -> vga v p where
vga v p = [C.exp|int{VecGetArray($(Vec v), $(PetscScalar** p))}|]
-- PETSC_EXTERN PetscErrorCode VecRestoreArray(Vec,PetscScalar**);
vecRestoreArrayPtr' :: Vec -> Ptr PetscScalar_ -> IO CInt
vecRestoreArrayPtr' v c = with c $ \pc -> vra v pc
where
vra w pc = [C.exp|int{VecRestoreArray($(Vec w), $(PetscScalar** pc))}|]
さらに、IIUC、コードはベクターの 2 つの追加のコピーを作成します。1 つは凍結時、もう 1 つは解凍時です。、しかし、それは非効率的だと思います。誰かが改善や簡素化を提案できますか?