我跟随NLPWP计算语言学网站并尝试创建一个Haskell程序来查找搭配(两个单词的最常见分组,例如"美国"或"以查找&# 34;)在一个单词列表中。我已经获得了以下工作代码来查找bigram频率:
import Data.Map (Map)
import qualified Data.Map as Map
-- | Function for creating a list of bigrams
-- | e.g. [("Colorless", "green"), ("green", "ideas")]
bigram :: [a] -> [[a]]
bigram [] = []
bigram [_] = []
bigram xs = take 2 xs : bigram (tail xs)
-- | Helper for freqList and freqBigram
countElem base alow = case (Map.lookup alow base) of
Just v -> Map.insert alow (v + 1) base
Nothing -> Map.insert alow 1 base
-- | Maps each word to its frequency.
freqList alow = foldl countElem Map.empty alow
-- | Maps each bigram to its frequency.
freqBigram alow = foldl countElem Map.empty (bigram alow)
我试图编写一个函数,将每个bigram的Map输出到[bigram of freram] / [(freq word 1)*(freq word 2)]。你能提供一些如何处理它的建议吗?
以下代码都没有起作用,但它给出了我想要做的事情的模糊概述。
collocations alow =
| let f key = (Map.lookup key freqBi) / ((Map.lookup (first alow) freqs)*(Map.lookup (last alow) freqs))
in Map.mapWithKey f = freqBi
where freqs = (freqList alow)
where freqBi = (freqBigram alow)
我对Haskell很新,所以如果您对如何修复搭配程序有任何想法,请告诉我。风格窍门也很受欢迎。
答案 0 :(得分:1)
当我读到它时,你的困惑源于错误的类型,或多或少。一般建议:在所有顶级函数上使用类型签名,并确保它们是合理的以及您对函数的期望(我经常在实现函数之前执行此操作)。
让我们来看看你的
-- | Function for creating a list of bigrams
-- | e.g. [("Colorless", "green"), ("green", "ideas")]
bigram :: [a] -> [[a]]
如果您要提供字符串列表,那么您将获得字符串列表的列表,因此您的二元组是一个列表。 您可以决定更明确(至少允许字符串而不是某些类型 - 至少为开头)。所以,实际上我们得到一个单词列表,从中得到一个Bigrams列表:
type Word = String
type Bigram = (Word, Word)
bigram :: [Word] -> [Bigram]
对于实现,您可以尝试使用Data.List中的现成函数,例如zipWith和tail。
现在你的freqList和freqBigram看起来像
freqList :: [Word] -> Map Word Int
freqBigram :: [Word] -> Map Bigram Int
使用此错误编译器的消息将更清楚。指出它:注意你在查找单词频率时所做的事情。你正在搜索word1和word2的频率,而bigram是(word1,word2)。
现在你应该能够自己解决这个问题了,我想。
答案 1 :(得分:0)
除了最终的colloctions
函数外,您的大多数代码都看起来很清晰。
我不确定为什么在等号后面有一个迷路管。你不是想写任何一种模式守卫,所以我认为不应该存在。
Map.lookup
会返回一个Maybe
键,因此尝试进行除法或乘法不会起作用。也许你想要的是某种带有键和地图的函数,如果键不存在则返回相关的计数或零?
除此之外,看起来你的工作并不算太远。
答案 2 :(得分:0)
首先,我建议你看一下这个功能
insertWith :: Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
也许你会认识到使用的模式
f freqs bg = insertWith (+) bg 1 freqs
接下来@MathematicalOrchid已经指出你的解决方案距离正确不太远。
lookup :: Ord k => k -> Map k a -> Maybe a
您已经在countElems
函数中处理了这个问题。
我要注意的是,有一个名为Applicative
的简洁抽象,对于像你这样的问题非常有用。
首先,您必须import Control.Applicative
如果您在7.10之前使用GHC用于较新版本,那么它已经触手可及。
那么这个抽象提供了什么,类似于Functor
它为你提供了一种处理副作用的方法"在您的情况下,查找失败的可能性导致Nothing
。
我们有Applicative
提供的两个运算符:pure
和<*>
,此外每个Applicative
都必须是Functor
我们也会得到后者的fmap
或<$>
只是为了方便而使用的中缀别名。
<*> :: Applicative f => f (a -> b) -> f a -> f b
<$> :: Functor f => a -> b -> f a -> f b
首先,您会看到这两个看起来很相似,但<*>
稍微不那么熟悉。
现在有一个功能
f :: Int -> Int
f x = x + 3
和 x1 ::可能是Int x1 =只是4 x2 ::也许Int x2 =没什么
一个人不能只是f y
因为那不会发生类型检查 - 但这是第一个要牢记的想法。 Maybe
是Functor
(它也是Applicative
- 它更像是M-thing
,但我们不会去那里。
f <$> x1 = Just 7
f <$> x2 = Nothing
所以你可以想象f
查找价值并在Just
内执行计算,如果没有价值 - 也就是说我们有Nothing
的情况,我们&#39;我会做每个懒惰学生做的事情 - 懒惰而什么都不做; - )。
现在我们进入下一部分<*>
g1 :: Maybe (Int -> Int)
g1 = Just (x + 3)
g2 :: Maybe (Int -> Int)
g2 = Nothing
仍然g1 x1
无效,但
g1 <*> x1 = Just 7
g1 <*> x2 = Nothing
g2 <*> x1 = Nothing -- remember g2 is Nothing
g2 <*> x2 = Nothing
魔法&#39;正在使用两个运算符...用于多参数函数
h :: Int -> Int -> Int
h x y = x + y + 2
和部分函数应用程序,只是意味着放入一个值,返回一个等待下一个值的函数。
GHCi> :type h 1
h 1 :: Int -> Int
现在奇怪的是,我们可以使用像h
这样的函数。
GHCi> :type h1 <$> x1
h1 <$> x1 :: Maybe (Int -> Int)
这很好,因为我们可以使用我们的<*>
y1 :: Maybe Int
y1 = Just 7
h1 <$> x1 <*> y1 = Just (4 + 7 + 2)
= Just 13
这甚至适用于任意数量的参数
k :: Int -> Int -> Int -> Int -> Int
k x y z w = ...
k <$> x1 <*> y1 <*> z1 <*> w1 = ...
所以设计一个纯函数,可以使用Int
,Float
,Double
或任何你喜欢的函数,然后使用Functor
/ Applicative
抽象来制作您的lookup
和频率计算相互协作。