我正在学习Haskell.
学习新语言时,我的标准技术之一是实现Hello World遗传算法,该算法尝试通过与某些输入字符串匹配的遗传算法技术生成字符串.
由于我对Haskell的经验不足(我必须比较最接近的是Kotlin)我搜索了一个示例代码,因此我可以将我对基本遗传算法的现有理解与代码相匹配,并根据我已经在进行的阅读对Haskell的一些理解.对语言的研究.
我遇到了这个教程:https://www.arcadianvisions.com/blog/2011/haskell-genetic-algorithm-hello-world.html
我设置了我的环境之后我在原子中转录它,每个部分我都不明白我做了一个快速谷歌,如果我在15分钟后不理解语法/语义我将继续抄写意图后来赶上那些特殊的部分.
所以,我理解了大部分代码,例如函数应用程序的顺序,monads(我认为我几乎与monads一起),数据类型,函数类型,currying,类型替换等.但是,有几个语法位/语义,我在阅读/研究中没有看到,不知道他们做了什么,但他们在我上面链接的代码示例中出现了很多.我希望有人可以向我解释一下:
(++)
(:)
<$>
<*>
(,)
(x !!)
p@(info@())
Run Code Online (Sandbox Code Playgroud)
我假设()和<>是一些特殊的语法,其中的东西是语义的?当我将它们悬停在原子上时(我正在使用atom-haskell-ghc)我可以看到Functor f => Applicative (f :: * -> *) where <*> :: f (a -> b) -> f a -> f b哪种类型看起来像monad,但我并不真正理解消费端的奇怪语法/语义.我是否应该将这些视为另一个函数(带有奇怪的中缀别名?).
以下是展示上述几个例子的具体行:
mate :: RandomGen g => Gene -> Gene -> Rand g Gene
mate gene1 gene2 = (++) <$> flip take gene1 <*> flip drop gene2 <$> pivot
where pivot = getRandomR (0, length gene1 - 1)
Run Code Online (Sandbox Code Playgroud)
在Haskell可以定义具有作为标识符包裹括号之间符号的序列,像例如一个功能(++)或(:)这是一个操作员可以同时用作一个函数,像(++) x y,并且作为中缀运算符,像x ++ y.在窗帘后面,Haskell编译器会将中缀运算符转换为函数调用,因此x ++ y完全等效(++) x y(除了运算符具有不同的优先级规则).
(++)这是append函数:(++) :: [a] -> [a] -> [a]它将两个列表作为输入,并构造一个相同类型元素的列表,其中包含第一个列表的元素,后跟第二个列表的元素.例如:
(++) [1, 4, 2, 5] [1, 3, 0, 2] == [1, 4, 2, 5, 1, 3, 0, 2]
Run Code Online (Sandbox Code Playgroud)
(:)这是列表类型的构造函数[a].它有类型(:) :: a -> [a] -> [a].它将元素和元素列表(所有相同类型)作为输入,它构造一个以第一个元素开头的列表,后跟第二个参数的元素.例如:
(:) 1 [4, 2, 5] = [1, 4, 2, 5]
Run Code Online (Sandbox Code Playgroud)
(<$>)你在你的问题中写道<$>,但正如你可能想到的那样,这意味着在某个地方(<$>) :: Functor f => (a -> b) -> f a -> f b定义了一个函数.
A Functor是类型类.Haskell中的几种类型是仿函数.最简单的是列表[],和Maybe.将(<$>)函数f :: a -> b和函子实例作为输入,例如列表[a].然后它会将其转换为仿函数实例[b].这是如何完成的,取决于Functor实例的实现方式((<$>)对于a Maybe具有不同于语义的语义[]).
虽然类比不完整,但您Functor有时可以看到元素的集合(Maybe基本上是零Nothing或一个Just x元素的集合).然后,它将通过函数引导这些元素,从而映射集合包含的元素,例如:
(+1) <$> [1, 4, 2, 5] == [2, 5, 3, 6]
(+1) <$> Nothing == Nothing
(+1) <$> (Just 2) == Just 3
Run Code Online (Sandbox Code Playgroud)
(<*>)这个功能(<*>) :: Applicative f => f (a -> b) -> f a -> f b有点难以理解.它使用了一个Applicative类型类.
作为实例的类型Applicative必须实现两个函数:pure :: Applicative a => a -> f a和(<*>) :: Applicative f => f (a -> b) -> f a -> f b(或程序员可以决定实现liftA2,但让我们在这里忽略它).
您再次可以看到Applicative(至少对于流行的实例)作为集合(如[]或Maybe).因此,我们将这些函数集合(所有类型a -> b)和as 集合作为输入.然后我们"乘以"这些,例如对于列表:
[f1, f2, ..., fm] <*> [x1, x2, ..., xn]
== [f1 x1, f1 x2, ..., f1 xn,
f2 x1, f2 x2, ..., f2 xn,
...,
fm x1, fm x2, ..., fm xn]
Run Code Online (Sandbox Code Playgroud)
所以这意味着例如Maybe,如果左操作数是Nothing,或右操作数是Nothing,或两者都是Nothing,那么这导致a Nothing,如果两者都是Justs(so Just f <*> Just x),那么我们得到一个Just (f x):
Just f <*> Just x == Just (f x)
Just f <*> Nothing == Nothing
Nothing <*> Just x == Nothing
Nothing <*> Nothing == Nothing
Run Code Online (Sandbox Code Playgroud)
(,)这是2元组的构造函数:(,) :: a -> b -> (a,b)因此将输入a a和a 作为输入b,并构造一个2元组,其中第一个项是第一个参数,第二个项是第二个参数.例如:
(,) 4 'a' == (4, 'a')
Run Code Online (Sandbox Code Playgroud)
(x !!)这是中缀运算符的一部分.您可以使用中缀运算符,例如指定左侧或右侧部分.在这种情况下,您构造了一个部分应用的函数.例如:
([1, 4, 2, 5] !!) == (!!) [1, 4, 2, 5]
(!! 2) == flip (!!) 2
Run Code Online (Sandbox Code Playgroud)
因此,对于后者,这意味着我们构造了一个函数,该函数将一个参数作为输入,该参数将作为左操作数填充.所以:
(!! 2) [1, 4, 2, 5] == (!!) [1, 4, 2, 5]
Run Code Online (Sandbox Code Playgroud)
该(!!) :: [a] -> Int -> a函数将列表作为输入,Int并且它返回该索引处的元素(从零开始的索引).
p@(info@())与上述相反,@它不是函数或运算符(这些实际上是相同的),而是关键字.
它用于模式匹配以获取对模式的引用,例如匹配子模式(或获取对子模式的引用).
例如,假设我们想要模式匹配2元组,并且我们想要引用整个元组,并且第一个元素,我们可以使用:
somefunction total@(left, _) = ...
Run Code Online (Sandbox Code Playgroud)
因此,如果我们再打电话somefunction (4, 'a'),那么这意味着它total会持有(4, 'a'),并且left会持有4.
这些大多是常规功能.
这<>不是特殊语法,只是函数名称的一部分.
在()定期的括号哪个组的事情,并确定优先像大多数其他语言,重要的注意的是,当你想引用一个操作函数(如++)它必须是在括号中.
++是列表连接函数 [1,2] ++ [3,4] = [1,2,3,4],或不使用中缀表示法(++) [1,2] [3,4] = [1,2,3,4]
:是'cons'函数,它将元素添加到列表1 : [2, 3, 4] = [1,2,3,4]或(:) 1 [2, 3, 4] = [1,2,3,4]没有中缀表示法.
<$> 是中缀运算符的别名 fmap
<*> 是应用程序的应用程序
, 是元组构造函数 (,) 1 2 = (1, 2)
!!是列表索引函数[1,2,3] !! 1 = 2.请注意,由于这些是单链表,因此索引是O(n)操作.
@用于定义"作为模式".模式匹配时,它允许您为参数指定名称,同时还使用模式匹配对其进行解构.例如,模式f (xs@[x1, x2])匹配两个元素列表,您可以使用x1和x2引用单个元素并xs作为整体引用列表