0

私はサーバー側のプログラミング言語をPHPからHaskellに変更する作業をしているため、関数型言語の設計に完全に精通していません。

ここでは、次のphp関数のような処理コードの一部をHaskellに変換したいと思います。

function loop($A) {
  $array1 = array();
  $num = (int)$A;
  if ($num == 0)
    $array1 = "0";
  for (int $i=0; $i<$num; $i++)
    foreach (loop($i-$num) as &$value)
      $array1[] = "+ ".$value." ";  //concat
  return $array1;
}

map / mapM_関数が多用されると思いますが、累積はどのように機能しますか?私は、追加された安全性は、この時点で変革する価値がないと考え始めています。

ありがとう。

4

2 に答える 2

6

これが私が答えている時点でのphpです:

function loop($A) {
  $array1 = array();
  $num = (int)$A;
  if ($num == 0)
    $array1 = "0";
  for (int $i=0; $i<$num; $i++)
    foreach (loop($i-$num) as &$value)
      $array1[] = "+ ".$value." ";  //concat
  return $array1;
}

上記のコードをHaskellに直訳すると、次のようになります。

loop :: Int -> [String]
loop 0 = ["0"]
loop num = ["+ " ++ value ++ " " | i <- [0..num], value <- loop (i - num)]

それでも何も達成されませんが、リスト内包表記でのHaskellの「ループ」または反復のフレーバーを提供します。

最初に気付くのは、型署名がloop :: Int -> [String]すぐに私に書くように警告したloop 0 = ["0"]ことloop 0 = "0"ですが、それは文字通りあなたが持っていたものではありませんが、Haskellは、ループの出力が文字列のリストである場合と単なる文字列である場合は一貫性がないことを発見しました。学ぶ価値のあるこの種のエラーチェックだと思います-おそらく、改訂時に書いたすべてのコードはphpでコンパイルされますが、設計の不整合のため、Haskellはコンパイルしません-それはあなたが書くときに明確に考えてバグを修正することを強制しますコンパイル時の前。

Haskellの完全な説明としてではなく、テイスターとして、そしてそれをあなたのコードに関連付けることで、私のコードを説明します:

loop :: Int -> [String]

loopIntegerを受け取り、文字列のリストを返す関数です。Haskellは現在、これを他のデータ型で使用することを許可していません。つまり、タイプセーフであり、箱から出してすぐにあらゆる種類の厄介なバグから保護されます。型システムは実際には非常に柔軟性があり、表現力があり、強力であり、Haskellが大好きな理由の1つですが、ここでは関数をロックするために使用しています。

(リストは配列の代わりにHaskellで広く使用されています。リストは非常に便利で、シーケンシャルアクセスで高速に動作します。配列データ型はありますが、あまり美しくありません/ Haskellish-最初にリストに慣れてください。)

loop 0 = ["0"]

loopこれは、Intを使用して呼び出す場合0、1つの文字列「0」でリストを返す必要があることを意味します。

loop num = [ something | stuff... ]

stuff変数の値を内部で取得して返すことができることを意味しますsomething

loop num = [ "+ " ++ value ++ " " | stuff... ]

(Haskellのバージョンの)を返すことを意味します"+ ".$value." "。ここでは++、phpの代わりにを使用します.

i <- [0..num]

これは、からまでiの範囲を許可することを意味します。それは理由である必要があります。0numIntnumloop :: Int -> ...

value <- loop (i - num)

これは、番号を使用してvalue呼び出した結果全体に範囲を設定することを意味します。 なぜなら。_loopi - numvalueStringloop :: Int -> [String]

Haskellでは、再帰呼び出しが式の書き換えとして実装されているようなものです。したがって、再帰呼び出しは呼び出されると消え、計算が本質的にスタックを乱雑にしない限り、スタックを乱雑にしません。

それをまとめると、次のようになります。

loop num = ["+ " ++ value ++ " " | i <- [0..num], value <- loop (i - num)]

その表記が非常に奇妙に感じる場合は、より命令型の構文に慣れている可能性があります。

loop :: Int -> [String]
loop 0 = ["0"]
loop num = do
    i <- [0..num]
    value <- loop (i - num)
    return ("+ " ++ value ++ " ")

もちろん、それでも有用なことは何もしませんが、少なくとも追跡するのは簡単です。

Learn You a Haskell for Great Goodhttp://learnyouahaskell.com/)またはReal World Haskellhttp://book.realworldhaskell.org/ )を使用して、簡単に始めて深く掘り下げてみませんか。Haskellは、大量のOKコードを置き換えるために、少量の正しいコードを書くための優れた方法だと思います。より明確に考え、書く量を減らしてください!

于 2012-09-11T00:08:31.290 に答える
3

ある種の辞書を持っているようです。辞書の典型的なHaskellデータ構造は、Mapによって提供されるData.Mapです。したがって、キーがStringsで、値がIntsの場合、マップのタイプはになりますMap String Int

ここで、コードから判断すると、同じ配列内でキーと値が混在しているように見えるため、最初にそれらをキーと値のペアに分離する必要があります。ここで書いていることはすべてHaskell配列でも完全に機能しますが、最初の入力は配列ではなくリストであると仮定します。

fix :: [a] -> [(a, a)]
fix [] = []
fix [_] = []
fix (k:v:xs) = (k, v):fix xs

アソシエーションリストで停止することを選択できます。その場合、それを説明的な文字列のリストに変換できます。

describe :: (Show k, Show v) => [(k, v)] -> [String]
describe xs = map (\(k, v) -> "Stored On: " ++ show k ++ ", item: " ++ show v) xs

または、直接印刷することもできます。

printPairs :: (Show k, Show v) => [(k, v)] -> IO ()
printPairs xs = mapM_ putStrLn (describe xs)

さて、より効率的なルックアップのために、通常は関連付けリストをとして保存したいMapので、使用するのは次のfromList関数だけですData.Map

fromList :: Ord k => [(k, v)] -> Map k v

また、リストをマップから削除したい場合(つまり、リストを印刷したい場合)は、次を使用するだけですtoList

toList :: Map k v -> [(k, v)]
于 2012-09-10T16:09:41.470 に答える