Haskell - 从输入的单词列表中查找bigrams

时间:2015-11-11 07:47:24

标签: haskell nlp corpus

我跟随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很新,所以如果您对如何修复搭配程序有任何想法,请告诉我。风格窍门也很受欢迎。

3 个答案:

答案 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因为那不会发生类型检查 - 但这是第一个要牢记的想法。 MaybeFunctor(它也是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

NEAT! - 但这仍然是如何解决你的问题的?

魔法&#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 = ...

所以设计一个纯函数,可以使用IntFloatDouble或任何你喜欢的函数,然后使用Functor / Applicative抽象来制作您的lookup和频率计算相互协作。