haskell

Haskell教程:#14 Zippers

Zippers Haskell 中的拉链基本上是指向数据结构(例如树)的某个特定位置的指针。 让我们考虑具有 5 个元素 [45,7,55,120,56] 的树,它可以表示为完美二叉树。如果我想更新这个列表的最后一个元素,那么在更新它之前我需要遍历所有元素以到达最后一个元素。对?但是,如果我们能够以这样一种方式构建我们的树,即具有 N 个元素的树是 [(N-1),N] 的集合,那会怎样呢?然后,我们不需要遍历所有不需要的 (N-1) 元素。我们可以直接更新第 N 个元素。这正是拉链的概念。它聚焦或指向树的特定位置,我们可以在该位置更新该值,而无需遍历整棵树。 在下面的例子中,我们在列表中实现了拉链的概念。同样,可以在树或文件数据结构中实现 Zipper。 data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord) type Zipper_List a = ([a],[a]) go_Forward :: Zipper_List a -> Zipper_List a go_Forward (x:xs, bs) = (xs, x:bs) go_Back :: Zipper_List a -> Zipper_List a go_Back (xs, b:bs) = (b:xs, bs) main = do let list_Ex = [1,2,3,4] print(go_Forward (list_Ex,[])) print(go_Back([4],[3,2,1])) 当您编译并执行上述程序时,它将产生以下输出 -

Continue reading

Haskell教程:#13 Monads

Monads Monad 只不过是一种具有一些额外功能的 Applicative Functor。它是一个 Type 类,它管理着三个基本规则,称为 monadic 规则。所有三个规则都严格适用于 Monad 声明,如下所示 class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b x >> y = x >>= \_ -> y fail :: String -> m a fail msg = error msg 适用于 Monad 声明的三项基本法律是 Left Identity Law - 返回函数不会改变值,也不应该改变 Monad 中的任何内容。可以表示为“return »> mf = mf”。 Right Identity Law - 返回函数不会改变值,也不应该改变 Monad 中的任何内容。可以表示为“mf >=> return = mf”。 Associativity - 根据这个定律,Functors 和 Monad 实例应该以相同的方式工作。它可以在数学上表示为“( f >==>g) >=> h =f >= >(g >=h)”。 前两个定律迭代相同的点,即返回值应该在绑定运算符的两侧具有标识行为。我们在前面的例子中已经使用了很多 Monad,却没有意识到它们是 Monad。考虑以下示例,其中我们使用 List Monad 生成特定列表。

Continue reading

Haskell教程:#12 Functor

函子(Functor) Haskell 中的函子是一种可以映射的不同类型的函数表示。 它是实现多态的高级概念。 根据 Haskell 开发者的说法,List、Map、Tree 等所有类型都是 Haskell Functor 的实例。Functor 是一个内置类,其函数定义类似于 class Functor f where fmap :: (a -> b) -> f a -> f b 根据这个定义,我们可以得出结论,Functor 是一个函数,它接受一个函数,比如 fmap() 并返回另一个函数。 在上面的例子中, fmap() 是函数 map() 的一般化表示。在下面的例子中,我们将看到 Haskell Functor 是如何工作的。 main = do print(map (subtract 1) [2,4,8,16]) print(fmap (subtract 1) [2,4,8,16]) 在这里,我们在列表上使用了 map() 和 fmap() 进行减法运算。 您可以观察到这两个语句将产生包含元素 [1,3,7,15] 的列表的相同结果。这两个函数都调用了另一个名为subtract() 的函数来产生结果。 [1,3,7,15] [1,3,7,15] 那么,map和fmap有什么区别呢? 不同之处在于它们的用法。 Functor 使我们能够在不同的数据类型中实现更多的功能主义者,例如“just”和“Nothing”。 main = do print (fmap (+7)(Just 10)) print (fmap (+7) Nothing) 上面的代码将在终端上产生以下输出

Continue reading

Haskell教程:#11 输入&输出

输入 & 输出 到目前为止,我们讨论的所有示例本质上都是静态的。在本章中,我们将学习与用户进行动态通信。我们将学习 Haskell 中使用的不同输入和输出技术。 文件和流(File & Streams) 到目前为止,我们已经对程序本身的所有输入进行了硬编码。我们一直在从静态变量中获取输入。现在,让我们学习如何从外部文件读取和写入。让我们创建一个文件并将其命名为“abc.txt”。接下来,在此文本文件中输入以下几行:“在这里,您将获得学习 Haskell 的最佳资源。” 接下来,我们将编写以下代码,该代码将在控制台上显示此文件的内容。在这里,我们使用函数 readFile() 读取文件直到找到 EOF 字符。 main = do let file = "abc.txt" contents <- readFile file putStrLn contents 上面的代码将文件“abc.txt”作为字符串读取,直到遇到任何文件结束字符。这段代码将生成以下输出。 在这里,您将获得学习 Haskell 的最佳资源。 命令行参数 Haskell 还提供了通过命令提示符操作文件的工具。让我们回到终端并输入“ghci”。然后,键入以下命令集 - let file = "abc.txt" writeFile file "I am just experimenting here." readFile file 在这里,我们创建了一个名为“abc.txt”的文本文件。接下来,我们使用命令 writeFile 在文件中插入了一条语句。最后,我们使用命令 readFile 在控制台上打印文件的内容。我们的代码将产生以下输出 - I am just experimenting here. 例外 异常可以被视为代码中的错误。这是编译器在运行时没有得到预期输出的情况。像任何其他优秀的编程语言一样,Haskell 提供了一种实现异常处理的方法。 如果您熟悉 Java,那么您可能知道 Try-Catch 块,我们通常会在该块中抛出错误并在 catch 块中捕获相同的错误。在 Haskell 中,我们也有相同的函数来捕获运行时错误。

Continue reading

Haskell教程:#10 模块

模块(Modules) 如果您使用过 Java,那么您就会知道所有类是如何绑定到一个名为 package.json 的文件夹中的。 同样,Haskell 可以被视为模块的集合。Haskell 是一种函数式语言,一切都表示为表达式,因此模块可以称为相似或相关类型函数的集合。您可以将一个模块中的函数导入到另一个模块中。 在开始定义其他函数之前,所有“import”语句都应该放在第一位。 在本章中,我们将学习 Haskell 模块的不同特性。 List Module List 提供了一些很棒的函数来处理列表类型的数据。 导入 List 模块后,您可以使用多种功能。在以下示例中,我们使用了 List 模块下的一些重要功能。 import Data.List main = do putStrLn("Different methods of List Module") print(intersperse '.' "Tutorialspoint.com") print(intercalate " " ["Lets","Start","with","Haskell"]) print(splitAt 7 "HaskellTutorial") print (sort [8,5,3,2,1,6,4,2]) 在这里,我们有很多函数甚至没有定义它们。 这是因为这些函数在 List 模块中可用。 导入 List 模块后,Haskell 编译器使所有这些函数在全局命名空间中可用。 因此,我们可以使用这些函数。我们的代码将产生以下输出 Different methods of List Module "T.u.t.o.r.i.a.l.s.p.o.i.n.t...c.o.m" "Lets Start with Haskell" ("Haskell","Tutorial") [1,2,2,3,4,5,6,8] Char Module Char 模块有很多预定义的函数来处理 Character 类型。 看看下面的代码块

Continue reading

Haskell教程:#9 函数组合

函数组合(Function Composition) 函数组合是将一个函数的输出用作另一个函数的输入的过程。 如果我们学习组合背后的数学会更好。 在数学中,合成用 f{g(x)} 表示,其中 g() 是一个函数,其输出用作另一个函数的输入,即 f()。如果一个函数的输出类型与第二个函数的输入类型匹配,则可以使用任意两个函数来实现函数组合。 我们使用点运算符 (.) 来实现 Haskell 中的函数组合。 看看下面的示例代码。 在这里,我们使用了函数组合来计算输入数是偶数还是奇数。 eveno :: Int -> Bool noto :: Bool -> String eveno x = if x `rem` 2 == 0 then True else False noto x = if x == True then "This is an even Number" else "This is an ODD number" main = do putStrLn "Example of Haskell Function composition" print ((noto.eveno)(16)) 在这里,在 main 函数中,我们同时调用了两个函数 noto 和 eveno。 编译器将首先以 16 作为参数调用函数“eveno()”。 此后,编译器将使用 eveno 方法的输出作为 noto() 方法的输入。它的输出如下 -

Continue reading

Haskell教程:#8 更高函数

更多函数 到目前为止,我们已经讨论了多种类型的 Haskell 函数并使用不同的方式来调用这些函数。 在本章中,我们将学习一些无需导入任何特殊 Type 类即可在 Haskell 中轻松使用的基本函数。 大多数这些函数是其他高阶函数的一部分。 Head 函数 Head 函数适用于列表。 它返回输入参数的第一个,它基本上是一个列表。 在下面的示例中,我们传递了一个包含 10 个值的列表,并使用 head 函数生成该列表的第一个元素。 main = do let x = [1..10] putStrLn "Our list is:" print (x) putStrLn "The first element of the list is:" print (head x) 上面代码运行结果如下 Our list is: [1,2,3,4,5,6,7,8,9,10] The first element of the list is: 1 Tail 函数 Tail 是补充 head 功能的功能。 它接受一个列表作为输入并生成没有头部部分的整个列表。 这意味着,tail 函数返回没有第一个元素的整个列表。 看看下面的例子 main = do let x = [1.

Continue reading

Haskell教程:#7 函数

Function 函数在 Haskell 中扮演着重要角色,因为它是一种函数式编程语言。 和其他语言一样,Haskell 也有自己的函数定义和声明。函数声明由函数名称及其参数列表及其输出组成。函数定义是您实际定义函数的地方。 让我们以 add 函数的小例子来详细理解这个概念。 add :: Integer -> Integer -> Integer --function declaration add x y = x + y --function definition main = do putStrLn "The addition of the two numbers is:" print(add 2 5) --calling a function 在这里,我们在第一行和第二行中声明了我们的函数,我们编写了我们的实际函数,它将接受两个参数并产生一个整数类型的输出。像大多数其他语言一样,Haskell 从 main 方法开始编译代码。 我们的代码将生成以下输出 The addition of the two numbers is: 7 模式匹配 (Pattern Matching) 模式匹配是匹配特定类型表达式的过程。 它只不过是一种简化代码的技术。 这种技术可以实现到任何类型的 Type 类中。 If-Else 可以用作模式匹配的替代选项。模式匹配可以被认为是动态多态的一种变体,在运行时,可以根据参数列表执行不同的方法。 看看下面的代码块。 这里我们使用了模式匹配技术来计算一个数的阶乘。 fact :: Int -> Int fact 0 = 1 fact n = n * fact ( n - 1 ) main = do putStrLn "The factorial of 5 is:" print (fact 5) 我们都知道如何计算一个数的阶乘。 编译器将开始搜索带有参数的名为“fact”的函数。 如果参数不等于 0,那么该数字将继续调用相同的函数,并且比实际参数的数字小 1。当参数的模式与 0 完全匹配时,它将调用我们的模式,即“fact 0 = 1”。 我们的代码将产生以下输出

Continue reading

Haskell教程:#6 类型和类

类型和类(Types and Type Class) Haskell 是一种函数式语言,它是严格类型化的,这意味着整个应用程序中使用的数据类型将在编译时为编译器所知。 内置类型类 在 Haskell 中,每个语句都被视为一个数学表达式,这个表达式的类别称为类型。 您可以说“类型”是编译时使用的表达式的数据类型。要了解有关类型的更多信息,我们将使用 “:t” 命令。 在泛型中,Type 可以被认为是一个值,而 Type Class 可以被认为是一组相似类型的类型。 在本章中,我们将了解不同的内置类型。 Int Int 是表示 Integer 类型数据的类型类。 2147483647 到 -2147483647 范围内的每个整数都属于 Int 类型类。 在以下示例中,函数 fType() 将根据其定义的类型运行。 fType :: Int -> Int -> Int fType x y = x*x + y*y main = print (fType 2 4) 在这里,我们将函数 fType() 的类型设置为 int。 该函数采用两个 int 值并返回一个 int 值。 如果编译并执行这段代码,它将产生以下输出 sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts sh-4.

Continue reading

Haskell教程:#5 基本运算符

Basic Operations 在本章中,我们将了解 Haskell 中使用的不同运算符。 与其他编程语言一样,Haskell 可以智能地处理一些基本运算,如加法、减法、乘法等。在接下来的章节中,我们将了解有关不同运算符及其用法的更多信息。在本章中,我们将使用我们的在线平台 (https://www.tutorialspoint.com/codingground.htm) 在 Haskell 中使用不同的运算符。 请记住,我们仅使用整数类型数字,因为我们将在后续章节中了解有关十进制类型数字的更多信息。 Addition Operator 顾名思义,加法 (+) 运算符用于加法函数。 以下示例代码显示了如何在 Haskell 中将两个整数相加 main = do let var1 = 2 let var2 = 3 putStrLn "The addition of the two numbers is:" print(var1 + var2) 在上面的文件中,我们创建了两个单独的变量 var1 和 var2。 最后,我们使用加法运算符打印结果。 使用编译和执行按钮运行您的代码。此代码将在屏幕上产生以下输出 The addition of the two numbers is: 5 Subtraction Operator 顾名思义,此运算符用于减法运算。 以下示例代码显示了如何在 Haskell 中减去两个整数 main = do let var1 = 10 let var2 = 6 putStrLn "The Subtraction of the two numbers is:" print(var1 - var2) 在本例中,我们创建了两个变量 var1 和 var2。 此后,我们使用减法 (-) 运算符将两个值相减。此代码将在屏幕上产生以下输出

Continue reading