แปล Haskell (monadic) เป็น F#

ช่วยฉันแปลบล็อกต่อไปนี้ของรหัส Haskell ฟังก์ชัน run จะสร้างสตริงข้อความที่สอดคล้องกับ regex ที่กำหนดซึ่งสรุปเป็นรูปแบบ ประกาศประเภท Pattern ที่คุณเห็นด้านล่างในบล็อกของรหัส F# คุณสามารถทดสอบฟังก์ชัน run ได้เช่น

genex $ POr [PConcat [PEscape( DoPa 1) 'd'], PConcat [PEscape (DoPa 2) 'd']]

{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}
import qualified Data.Text as T
import qualified Control.Monad.Stream as Stream
import Text.Regex.TDFA.Pattern
import Control.Monad.State
import Control.Applicative

genex = Stream.toList . run

maxRepeat :: Int
maxRepeat = 3

each = foldl1 (<|>) . map return

run :: Pattern -> Stream.Stream T.Text
run p = case p of
    PBound low high p -> do
        n <- each [low..maybe (low+maxRepeat) id high]
        fmap T.concat . sequence $ replicate n (run p) 
    PConcat ps -> fmap T.concat . Stream.suspended . sequence $ map run ps
    POr xs -> foldl1 mplus $ map run xs
    PEscape {..} -> case getPatternChar of
        'd' -> chars $ ['0'..'9']
        'w' -> chars $ ['0'..'9'] ++ '_' : ['a'..'z'] ++ ['A'..'Z']
        ch  -> isChar ch
    _      -> error $ show p
    where
    isChar = return . T.singleton
    chars = each . map T.singleton

ด้านล่างฉันให้ความพยายามที่ไม่ดีของฉัน มันใช้งานได้แต่ไม่ถูกต้อง ปัญหาอยู่ในต่อไปนี้ สมมติว่า parse สร้าง รูปแบบ เช่นนั้น

แยกวิเคราะห์ "\\d\\d";; val it : Pattern = POr [PConcat [PEscape (DoPa 1,'d'); PEscape (DoPa 2, 'd')]]

และ

แยกวิเคราะห์ "\\d{2}";; val it : Pattern = POr [PConcat [PBound (2, บาง 2, PEscape (DoPa 1, 'd'))]]

ดังนั้นการป้อนทั้งสองรูปแบบให้ ทำงาน ฉันคาดว่าจะได้รับ seq [['2'; '2']; ['2'; '3']; ['2'; '1']; ['2'; '4']; ...] ที่สอดคล้องกับ seq ["22"; "23"; "21"; "24"; ...] (2 สัญลักษณ์ต่อสตริง)

สิ่งนี้ใช้ได้ในกรณีแรก

พ่อ [PConcat [PEscape (DoPa 1,'d'); PEscape (DoPa 2, 'd')]] |> วิ่ง;; ตรวจสอบมัน : seq = seq [['2'; '2']; ['2'; '3']; ['2'; '1']; ['2'; '4']; ...]

seq ["22"; "23"; "21"; "24"; ...]

แต่ไม่ใช่ในวินาที

พ่อ [PConcat [PBound (2, บาง 2, PEscape (DoPa 1, 'd'))]] |> run;; ตรวจสอบมัน : seq = seq [['2']; ['2']; ['2']; ['3']; ...]

seq ["2"; "2", "2"; "3", "2"; "1", "2"; "4";...] (1 สัญลักษณ์ต่อสตริง)

ฉันทดสอบตัวแปรต่างๆ ด้วยเงื่อนไขต่อไปนี้:

| POr ps                -> Seq.concat (List.map run ps)
| PConcat ps            -> (sequence (List.map (run >> Seq.concat) ps))
| PBound (low,high,p)   -> 

แต่ทั้งหมดก็เปล่าประโยชน์ ฉันไม่สามารถหาคำแปลที่ถูกต้องได้

- บางทีฉันควรใช้ String หรือ Array แทนรายการถ่าน

- และฉันคิดว่า Seq เป็นอะนาล็อกที่ค่อนข้างดีกับ Control.Monad.Stream ใช่มั้ย?

ขอบคุณล่วงหน้าสำหรับความช่วยเหลือ

open System

/// Used to track elements of the pattern that accept characters or are anchors
type DoPa = DoPa of int

/// Pattern is the type returned by the regular expression parser.
/// This is consumed by the CorePattern module and the tender leaves
/// are nibbled by the TNFA module.
type Pattern = PEmpty
             | POr     of Pattern list                  // flattened by starTrans
             | PConcat of Pattern list                  // flattened by starTrans
             | PBound  of int * (int option) * Pattern  // eliminated by starTrans
             | PEscape of DoPa * char                   // Backslashed Character

let maxRepeat = 3

let maybe deflt f opt = 
    match opt with
    | None -> deflt
    | Some v -> f v

/// Cartesian production
/// try in F# interactive: sequence [[1;2];[3;4]];;
let rec sequence = function 
  | []      -> Seq.singleton [] 
  | (l::ls) -> seq { for x in l do for xs in sequence ls do yield (x::xs) } 



let from'space'to'tilda     =  [' '..'~'] |> List.ofSeq
let numbers                 =  ['0'..'9'] |> List.ofSeq
let numbers'and'alphas      =  (['0'..'9'] @ '_' :: ['a'..'z'] @ ['A'..'Z']) |> List.ofSeq
let whites                  =  ['\009'; '\010'; '\012'; '\013'; '\032' ] |> List.ofSeq

let rec run (p:Pattern) : seq<char list> =
    let chars chs = seq { yield [for s in chs -> s] }
    match p with
    | POr ps                -> Seq.concat (List.map run ps)
    | PConcat ps            -> (sequence (List.map (run >> Seq.concat) ps))
    | PBound (low,high,p)   -> 
        let ns = seq {low .. maybe (low + maxRepeat) id high}
        Seq.concat (seq { for n in ns do yield  sequence (List.replicate n (((run >> Seq.concat)  p))) })
        // Seq.concat (seq { for n in ns do yield     ((List.replicate n (run p)) |> Seq.concat |> List.ofSeq |> sequence)})
        //((List.replicate low (run p)) |> Seq.concat |> List.ofSeq |> sequence)
        // PConcat [ for n in ns -> p] |> run
    | PEscape(_, ch)  -> 
        match ch with
        | 'd' -> chars numbers
        | 'w' -> chars numbers'and'alphas
        | ch  -> chars [ch]
    | _                     -> Seq.empty

person alexander.vladislav.popov    schedule 05.07.2012    source แหล่งที่มา
comment
คุณไม่มีตัวอย่างที่ง่ายกว่านี้เหรอ? - อย่างน้อยก็ลบโค้ดที่ไม่จำเป็นออก   -  person John Palmer    schedule 05.07.2012


คำตอบ (1)


ฉันไม่รู้ว่าทำไมคุณไม่แปล Data.Text จาก Haskell เป็น string ใน F# คุณเพียงแค่ต้องเลียนแบบสองฟังก์ชัน นอกเหนือจากนั้น ฉันได้ทำการเปลี่ยนแปลงเพียงเล็กน้อยเพื่อให้มันใช้งานได้ ด้วยวิธีนี้ คุณสามารถเปรียบเทียบมันกับโค้ดต้นฉบับของคุณได้อย่างง่ายดาย ดูโค้ดที่ถูกแทนที่ระหว่าง (* *)

open System

// Mimic Data.Text as T
module T =
    let concat (x:seq<_>) = System.String.Concat x
    let singleton (x:char) = string x


/// Used to track elements of the pattern that accept characters or are anchors
type DoPa = DoPa of int

/// Pattern is the type returned by the regular expression parser.
/// This is consumed by the CorePattern module and the tender leaves
/// are nibbled by the TNFA module.
type Pattern = PEmpty
             | POr     of Pattern list                  // flattened by starTrans
             | PConcat of Pattern list                  // flattened by starTrans
             | PBound  of int * (int option) * Pattern  // eliminated by starTrans
             | PEscape of DoPa * char                   // Backslashed Character

let maxRepeat = 3

let maybe deflt f opt = 
    match opt with
    | None -> deflt
    | Some v -> f v

/// Cartesian production
/// try in F# interactive: sequence [[1;2];[3;4]];;
let rec sequence = function 
  | []      -> Seq.singleton [] 
  | (l::ls) -> seq { for x in l do for xs in sequence ls do yield (x::xs) } 



let from'space'to'tilda     =  [' '..'~'] |> List.ofSeq
let numbers                 =  ['0'..'9'] |> List.ofSeq
let numbers'and'alphas      =  (['0'..'9'] @ '_' :: ['a'..'z'] @ ['A'..'Z']) |> List.ofSeq
let whites                  =  ['\009'; '\010'; '\012'; '\013'; '\032' ] |> List.ofSeq

let rec run (p:Pattern) (*: seq<char list> *) =
    (* let chars chs = seq { yield [for s in chs -> s] } *)
    let chars (chs:seq<char>)  = Seq.map string chs

    match p with
    | POr ps                -> Seq.concat (List.map run ps)
    | PConcat ps            -> Seq.map T.concat << sequence <| List.map run ps (* (sequence (List.map (run >> Seq.concat) ps)) *) 
    | PBound (low,high,p)   -> 
        seq {
        for n in [low..maybe (low+maxRepeat) id high] do
            yield! (  (Seq.map T.concat << sequence) (List.replicate n (run p)) )}
        (*let ns = seq {low .. maybe (low + maxRepeat) id high}
        Seq.concat (seq { for n in ns do yield  sequence (List.replicate n (((run >> Seq.concat)  p)))  *)

        // Seq.concat (seq { for n in ns do yield     ((List.replicate n (run p)) |> Seq.concat |> List.ofSeq |> sequence)})
        //((List.replicate low (run p)) |> Seq.concat |> List.ofSeq |> sequence)
        // PConcat [ for n in ns -> p] |> run
    | PEscape(_, ch)  -> 
        match ch with
        | 'd' -> chars numbers
        | 'w' -> chars numbers'and'alphas
        | ch  -> chars [ch]
    | _                     -> Seq.empty  


อัปเดต

หากคุณกำลังแปลโค้ด Haskell เป็น F# คุณอาจลองใช้โค้ดนี้ ซึ่งเลียนแบบ Haskell จำนวนมาก รวมถึงฟังก์ชันที่ใช้ Type Classes ฉันทำการทดสอบการแปลให้ใกล้เคียงกับโค้ด Haskell ดั้งเดิมของคุณมากที่สุด แต่ใช้ F# List (ไม่ขี้เกียจ) และมีลักษณะดังนี้:

#load "Prelude.fs"
#load "Monad.fs"
#load "Applicative.fs"
#load "Monoid.fs"

open Prelude
open Control.Monad.Base
open Control.Applicative

module T =
    let concat (x:list<_>) = System.String.Concat x
    let singleton (x:char) = string x

type DoPa = DoPa of int

type Pattern = PEmpty
             | POr     of Pattern list
             | PConcat of Pattern list
             | PBound  of int * (int option) * Pattern
             | PEscape of DoPa * char

let maxRepeat = 3

let inline each   x = foldl1 (<|>) << map return'  <| x

let rec run p:list<_> = 
    let inline isChar x = return' << T.singleton   <| x
    let inline chars  x = each << map T.singleton  <| x
    match p with
    | PBound (low,high,p) -> do' {
        let! n = each [low..maybe (low+maxRepeat) id high]
        return! (fmap T.concat << sequence <| replicate n (run p))}
    | PConcat ps -> fmap T.concat << sequence <| map run ps
    | POr xs -> foldl1 mplus <| map run xs
    | PEscape (_, ch) -> 
        match ch with
        | 'd' -> chars <| ['0'..'9']
        | 'w' -> chars <| ['0'..'9'] @ '_' :: ['a'..'z'] @ ['A'..'Z']
        | ch  -> isChar ch
    | _  -> failwith <| string p

let genex = run
person Gus    schedule 05.07.2012
comment
ขอบคุณมากจริงๆครับ คุณให้เบาะแสแก่ฉัน ฉันคงจะตายถ้าไม่มีมันอยู่ในเขาวงกตแห่งความคิดของฉัน :) - person alexander.vladislav.popov; 06.07.2012
comment
@ alexander.vladislav.popov หากคุณกำลังแปลโค้ดจาก Haskell เป็น F# โปรดดูการอัปเดต - person Gus; 07.07.2012