[英]What is a pseudocode implementation of this context-free language enumerator?
Haskell上的此博客文章介紹了如何借助名為Omega的單子語法來枚舉無上下文語法。
我之所以無法理解它的工作原理,部分原因是缺乏對該monad的工作原理的解釋,但主要是由於我聽不懂monad的事實。 沒有monad ,該算法的正確偽代碼解釋是什么?
最好使用類似於JavaScript或Python這樣的簡單通用語言的語法。
這是沒有單子的Haskell版本。 我確實使用列表推導,但是這些更直觀,您也可以在Python中使用它們。
Omega
類型只是[]
的包裝,但是有助於將“符號字符串”和“可能字符串列表”的概念分開。 由於我們不打算使用Omega
的“可能的字符串列表”,讓我們使用newtype
包裝器“的符號串”把一切都直:
import Prelude hiding (String)
-- represent a sequence of symbols of type `a`,
-- i.e. a string recognised by a grammar over `a`
newtype String a = String [a]
deriving Show
-- simple wrapper for (++) to also make things more explicit when we use it
joinStrings (String a1) (String a2) = String (a1 ++ a2)
這是博客文章中的Symbol
類型:
data Symbol a
= Terminal a
| Nonterminal [[Symbol a]] -- a disjunction of juxtapositions
Omega
monad的核心實際上是diagonal
函數:
-- | This is the hinge algorithm of the Omega monad,
-- exposed because it can be useful on its own. Joins
-- a list of lists with the property that for every x y
-- there is an n such that @xs !! x !! y == diagonal xs !! n@.
diagonal :: [[a]] -> [a]
鑒於此,從博客文章中enumerate
的是:
enumerate :: Symbol a -> Omega [a]
enumerate (Terminal a) = return [a]
enumerate (Nonterminal alts) = do
alt <- each alts -- for each alternative
-- (each is the Omega constructor :: [a] -> Omega a)
rep <- mapM enumerate alt -- enumerate each symbol in the sequence
return $ concat rep -- and concatenate the results
我們的enumerate
將具有以下類型:
enumerate :: Symbol a -> [String a]
Terminal
盒很簡單:
enumerate (Terminal a) = [String [a]]
在非Nonterminal
情況下,每個替代項的幫助函數將很有用:
-- Enumerate the strings accepted by a sequence of symbols
enumerateSymbols :: [Symbol a] -> [String a]
基本結果非常簡單,盡管結果不是[]
,但是包含空字符串的單例結果:
enumerateSymbols [] = [String []]
對於非空的情況,可以使用diagonal
,以所有可能的方式用另一個助手將頭部和尾部的弦配對起來:
crossProduct :: [a] -> [b] -> [(a, b)]
crossProduct as bs = diagonal [[(a, b) | b <- bs] | a <- as]
我也可以寫[[(a, b) | a <- as] | b <- bs]
[[(a, b) | a <- as] | b <- bs]
[[(a, b) | a <- as] | b <- bs]
但我選擇了另一個,因為最終復制了博客文章的輸出。
現在我們可以為enumerateSymbols
編寫非空的情況:
enumerateSymbols (sym:syms) =
let prefixes = enumerate sym
suffixes = enumerateSymbols syms
in [joinStrings prefix suffix
| (prefix, suffix) <- crossProduct prefixes suffixes]
現在是enumerate
的非空情況:
enumerate (Nonterminal alts) =
-- get the list of strings for each of the alternatives
let choices = map enumerateSymbols alts
-- and use diagonal to combine them in a "fair" way
in diagonal choices
這是來自歐米茄的diagonal
,有我的解釋:
diagonal = diagonal' 0
where
-- strip n xss returns two lists,
-- the first containing the head of each of the first n lists in xss,
-- the second containing the tail of the first n lists in xss
-- and all of the remaining lists in xss.
-- empty lists in xss are ignored
stripe 0 xss = ([],xss)
stripe n [] = ([],[])
stripe n ([]:xss) = stripe n xss
stripe n ((x:xs):xss) =
let (nstripe, nlists) = stripe (n-1) xss
in (x:nstripe, xs:nlists)
-- diagonal' n xss uses stripe n to split up
-- xss into a chunk of n elements representing the
-- nth diagonal of the original input, and the rest
-- of the original input for a recursive call to
-- diagonal' (n+1)
diagonal' _ [] = []
diagonal' n xss =
let (str, xss') = stripe n xss
in str ++ diagonal' (n+1) xss'
還值得閱讀有關對角化和無限結構的廣度優先搜索的一般概念的本文 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.