Haskell笔记1

前言

这个学期开始学习Haskelll(主要关于Codeworld和ghci),感觉很多东西和OOP不一样,最近感觉也应该开始记录点东西,以备日后可以查看,提高学习的效率。


1.animationOf:: (Double -> Picture)

这个函数接收一个 Double -> Picture 型的函数作为参数,并把此函数中的double变量视作时间变量,时间变量名字通常情况下命名为t,但也可以用其它任意名称。

2.函数作用的先后顺序

以下代码

import CodeWorld

main::IO()

main = drawingOf scene

scene  :: Picture

scene = (colored red (solidRectangle 2 2))& (colored blue (solidRectangle 3 3))

可以看到蓝色的矩形被红色矩形盖住了一部分,如果把他们的顺序对调即最后一句改为

scene = (colored blue (solidRectangle 2 2))&(colored red (solidRectangle 3 3))

就变成了红色被蓝色矩形盖住了一部分。

类似的还有

import CodeWorld

main::IO()

main = animationOf scene

scene :: Double->Picture

scene t = rotated t (translated 2 0 ((circle 1)&polyline [(0,-1),(0,1)]))&coordinatePlane

此处可以看到球以原点为圆心,2为半径旋转。

如果将rotated和transalated对调即

scene t = translated 2 0 (rotated t ((circle 1)&polyline [(0,-1),(0,1)]))&coordinatePlane

可以看到圆在(2,0)处自转。

为了更好的说明,rotated函数在codeworld中的解释如下

rotated :: HasCallStack => Double -> Picture -> Picture#

A picture rotated by this angle.

Angles are in radians.

它接收一个double型变量,并以这个变量作为弧度,以原点为圆心做旋转。正如第一个例子,我们对处于(2,0)的一个圆进行旋转,它就会围绕着原点转。但如果我们先构造了一个旋转的圆,之后对他进行平移,那么它将不再表现得与之前一样。这是因为rotated函数以作用完毕,它返回的Picture变量作为了translated的参数。

通过上面两个例子可以知道,函数的作用是在返回的那一刻就结束了,所以通过不同的顺序调用函数,会产生不同的效果。

3.应用List绘制多个图形

以下代码展示了绘制多个图形的一个方法

import CodeWorld

primes :: [Integer]

primes = sieve [2..]

  where sieve cs =

          let p = head cs

          in [ p ] ++ sieve [ c | c <- tail cs, c `mod` p /= 0 ]

g::[a]->[b]->[(a,b)]

g (x:xs) (y:ys) = (x,y):(zip xs ys)

g _ [] = []

g [] _ = []


f::[(Integer,Color)]->[Picture]

f ((a,b):as) = (translated (fromIntegral (fst(a,b))) 0.0 (colored (snd(a,b)) (solidRectangle 5 5))):(f as)

f [] = []

scene :: Int -> Picture

scene n = pictures(f(reverse (g (take n primes) (take n assortedColors))))

main :: IO ()

main = drawingOf (coordinatePlane & (scene 6))


重点为这一句

f ((a,b):as) = (translated (fromIntegral (fst(a,b))) 0.0 (colored (snd(a,b)) (solidRectangle 5 5))):(f as)

更直观的来说,针对绘制多个图形我们可以用如下几种方式

import CodeWorld

scene :: Int -> Picture

scene n = pictures[translated (fromIntegral x) 0.0 (rectangle 5 5)|x<-[0,3..3*n]]

main :: IO ()

main = drawingOf (coordinatePlane & (scene 6))

绘制了平移的图形,其中在List里,每一个x都会创造一个新的矩形。

为了让矩形区别的更加明显,我们给矩形涂上颜色。

import CodeWorld

scene :: Picture

scene = pictures[translated (fromIntegral x) 0.0 (colored y (solidRectangle 5 5))

                |x<-[0,3..12],y<-[red,green,purple,orange]]

main :: IO ()

main = drawingOf (coordinatePlane & scene)

有意思的是,如果用如上代码绘制图形,你可能期待它应该为4个矩形涂上4中不同的颜色,但却只看到了红色的矩形。

这是因为,每个x和y都会生成一个新的矩形,上面的代码实际上是在每个位置生成了4种不同颜色的矩形,由于遮挡关系,你只会看到第一个红色的矩形!

那么如何让每一个x和y产生对应关系呢?我们可以选择tuple来处理

上面代码修改为

scene = pictures[translated (fromIntegral x) 0.0 (colored y (solidRectangle 5 5))

                |(x,y)<-zip [0,3..12] [red,green,purple,orange]]

通过规定了x,y的对应关系,我们为每一个x涂上了颜色。

4.Recursion

由于Haskell是函数式编程,我们在函数定义中使用如i=i+1,n=n+f(x)之类的语句进行迭代。为了处理这种情况,我们需要使用另一种形式的recursion。

例如阶乘我们可以写成

f::Integer->Integer

f 0 = 1

f n = n * f n-1

由于我们定义了f 0的值,所以当迭代进行到0是将赋入1完成n*(n-1)*..*1的计算。

我们考虑另外一个问题:

{- A block digit sum combines several digits before summing up,

- beginning with the last position of the original number.

- For example, the 3-block digit sum of 1234567 is the number

- 1 + 234 + 567 = 802.

- An alternating digit sum switches between addition and

- subtraction, here in a fashion such that altogether no

- negative number is obtained. For example, the alternating

- 3-block digit sum of 1234567 is the number 1 - 234 + 567 = 334,

- while the alternating 2-block digit sum of 54321 is obtained

- as -5 + 43 - 21 = 17.

-

- Write a function that computes such generalized digit sums.

- The function is controlled by arguments for the block length

- and (as a Boolean value) the information whether or not an

- alternating digit sum is to be computed.

- Thus, for example:

-

-  genDigsum 3 False 1234567 = 802

-  genDigsum 3 True  1234567 = 334

-  genDigsum 2 True  54321  = 17

-}

对于分离成几个数字块,我们可以使用`quotRem`方法,它返回(q,r),q为商,r为余数。例如,1234`quotRem`我们将会得到(12,34),此时我们将它分离了一次,再对12进行一次处理即可得到(0,12)。至此,我们将1234分离为,12与34.

对应的数字块之间的加法我们应该如何实现?由于调用的函数在返回值之后就不再进行运算,我们需要对函数进行recursion处理。一个基本的OOP思路可以是在loop中进行运算,用一个参数存储所需要的中间值。但是由于Haskell不支持类似的操作。我们可以用一下迭代来实现:

normalDigsum::Integer->Integer->Integer

normalDigsum 0 p = 0

normalDigsum n p = r + (normalDigsum q p)

                where (q,r) = n`quotRem`(10^p)

仔细来看,normalDigsum将求和变为了r0+(r1+(r2+..)),这是可行的。但是对于alternat类型来说它却变成了r0+(r1-(r2+..))负号是错误的,那么我们该如规避?

我们可以为alternat型设立一个额外的参数,它专门用于存储结果,可以避免此类情况的发生,我喜欢叫它为“记忆参数”。实现代码如下:

alternatDigsum::Integer->Integer->Bool->Integer->Integer

alternatDigsum 0 p b c = c

alternatDigsum n p b c = if b==True

                    then alternatDigsum q p (not b) (c+r)

                    else alternatDigsum q p (not b) (c-r)

                    where (q,r) = n`quotRem`(10^p)

当数字块源为0时代表结束,我们可以返回c。在运算过程中,针对每一次不同的运算,在记忆参数中进行加法或者减法,由于每次调用函数时都会直接传递给它上一次的结果,因而保证了负号的准确性。

5.Indexitis与wholemeal programming

Indexitis是一个混合的自创词汇,Index+itis可以译作索引炎。它的意思是指需要对索引进行繁琐的操作,尤其会在修改时因为索引而引起错误。

wholemeal programming就是它的反意,在编程时对整个所需要处理的数据进行限定,在haskell中即利用List来行使loop职能。由于List修改时的简易,它会减少因为index错误而引发的种种问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,797评论 0 38
  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,149评论 0 13
  • CHAPTER 1: INTRODUCTION 第一章:简介 In this chapter, we discus...
    哈小奇阅读 1,030评论 2 1
  • 一、进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专...
    ZYZZZ阅读 252评论 1 1
  • 311 如果你足够优秀,一定要去找那些和你一样优秀或比你优秀的人才,如果你不够优秀,是很难吸引到优秀人才的。再说了...
    e1f1b6c637ae阅读 250评论 0 0