33

最近、Goプログラミング言語について読み始めましたが、チャネル変数が非常に魅力的な概念であることがわかりました。Haskellで同じ概念をエミュレートすることは可能ですか?たぶんChannel a、キーワードのように機能する可変状態と機能を有効にするために、データ型とモナド構造を持っているでしょうgo

私は並行プログラミングがあまり得意ではなく、Haskellのこのような単純なチャネル受け渡しメカニズムは私の人生を本当に楽にしてくれるでしょう。

編集

人々は私がHaskellに翻訳することに興味を持ったGoのパターンの種類を明確にするように私に頼みました。そのため、Goにはファーストクラスのチャネル変数があり、関数で受け渡しして返すことができます。これらのチャネルの読み取りと書き込みができるため、同時に実行できるルーチン間で簡単に通信できます。Goにはgoキーワードもあり、言語仕様に従って、独立したスレッドとして同時に関数の実行を開始し、待機せずにコードを実行し続けます。

私が興味を持っている正確なパターンは次のようなものです(Goの構文は奇妙です-変数は通常の逆の方法ではなくvarName varTypeによって宣言されます-しかし私はそれが読みやすいと思います):

func generateStep(ch chan int) {
      //ch is a variable of type chan int, which is a channel that comunicate integers
      for {
          ch <- randomInteger() //just sends random integers in the channel 
      }

func filter(input, output chan int) {
      state int
      for {
          step <- input  //reads an int from the input channel
          newstate := update(state, step) //update the variable with some update function
          if criteria(newstate, state) {
             state = newstate // if the newstate pass some criteria, accept the update
          } 
          output <- state    //pass it to the output channel
      } 
}

func main() {
    intChan := make(chan int) 
    mcChan  := make(chan int) 
    go generateStep(intChan)     // execute the channels concurrently
    go filter(intChan, mcChan)
    for i:=0; i<numSteps; i++  {
        x <- mcChan        // get values from the filtered channel
        accumulateStats(x)  // calculate some statistics
    } 
    printStatisticsAbout(x)
}

私の主な関心は、モンテカルロシミュレーションを実行することです。このシミュレーションでは、システムの現在の状態を変更しようとし、いくつかの基準を満たしている場合は変更を受け入れることで、構成を順番に生成します。

これらのチャネルのものを使用して、マルチコアプロセッサで並行して実行される非常にシンプルで読みやすく小さなモンテカルロシミュレーションを作成できたという事実は、私に本当に感銘を与えました。

問題は、Goにはいくつかの制限があることです(特に、Haskellで慣れている方法でポリモーフィズムが欠けています)。それに加えて、私はHaskellが本当に好きで、それを交換したくありません。したがって、問題は、上記のコードのようなメカニズムを使用して、Haskellで並行シミュレーションを簡単に実行する方法があるかどうかです。

EDIT(2、context): 私はコンピューターサイエンス、特に並行性については学んでいません。私は、CSとはまったく関係のない分野で、日常の研究ルーチンで単純な問題を解決するための単純なプログラムを作成するだけの人です。Haskellの仕組みが面白くて、それを使ってちょっとした雑用をするのが好きです。

パイ計算やCSPチャネルだけについて聞いたことはありません。質問が不適切に思われる場合は申し訳ありませんが、それはおそらく私の大きな無知のせいです。

そうです、私はGoのどのパターンをHaskellで複製したいかについてより具体的にする必要があります。そして、質問をより具体的に編集しようとします。しかし、深い理論的な質問を期待しないでください。問題は、私が読んでコーディングしたいくつかのことから、Goには並行性を実行するための優れた方法があるようです(私の場合、これは、すべてのコアを数値計算でハミングさせるという私の仕事が簡単であることを意味します)、そしてHaskellで同様の構文を使用できれば、嬉しいです。

4

2 に答える 2

37

あなたが探しているのは BaseのControl.Concurrent.Chanだと思います。私はそれが明らかなhaskellifications以外のgoのchansと何ら違うことを発見しませんでした。チャンネルは特別なものではありません。それについてはwikiページをご覧ください

チャネルはCommunicationSequentialProcess(CSP)と呼ばれるより一般的な概念の一部であり、HaskellでCSPのスタイルでプログラミングを行いたい場合は、Communicating Haskell Processes(CHP)パッケージを確認することをお勧めします。

CHPはHaskellで並行性を実現する唯一の方法です。詳細については、Haskellwikiの並行性ページを参照してください。あなたのユースケースはDataParrallelHaskellを使用して作成するのが最適だと思いますが、それは現在進行中の作業なので、今は別のものを使用することをお勧めします。

于 2010-12-23T20:56:08.397 に答える
2

HaskellElephantの答えを拡張すると、Control.Concurrent.Chanはチャネルを探す方法であり、Control.ConcurrentはキーワードforkIOをエミュレートできます。go構文をGoに少し似せるために、次のエイリアスのセットを使用できます。

import Control.Concurrent (forkIO)
import Control.Concurrent.Chan (newChan, readChan, writeChan)
import Control.Concurrent.MVar (newMVar, swapMVar, readMVar)

data GoChan a = GoChan { chan :: Chan a, closed :: MVar Bool }

go :: IO () -> IO ThreadId
go = forkIO

make :: IO (GoChan a)
make = do
    ch <- newChan
    cl <- newMVar False
    return $ GoChan ch cl

get :: GoChan a -> IO a
get ch = do
    cl <- readMVar $ closed ch
    if cl
        then error "Can't read from closed channel!"
        else readChan $ chan ch

(=->) :: a -> GoChan a -> IO ()
v =-> ch = do
    cl <- readMVar $ closed ch
    if cl
        then error "Can't write to closed channel!"
        else writeChan (chan ch) v

forRange :: GoChan a -> (a -> IO b) -> IO [b]
forRange ch func = fmap reverse $ range_ ch func []
    where range_ ch func acc = do
        cl <- readMVar $ closed ch
        if cl
            then return ()
            else do
                v <- get ch
                func v
                range_ ch func $ v : acc
close :: GoChan a -> IO ()
close ch = do
    swapMVar (closed ch) True
    return ()

これは次のように使用できます。

import Control.Monad

generate :: GoChan Int -> IO ()
generate c = do
    forM [1..100] (=-> c)
    close c

process :: GoChan Int -> IO ()
process c = forRange c print

main :: IO ()
main = do
    c <- make
    go $ generate c
    process c

(警告:テストされていないコード)

于 2016-07-09T00:54:43.693 に答える