Showing posts with label 学习笔记. Show all posts
Showing posts with label 学习笔记. Show all posts

November 8, 2007

复习 Perl

今天我去报名下一次托福模考,顺便去了书店转转——很长时间没关心计算机方面的书籍了,我唯一想看到只是《精通正则表达式》,也算是 “门当户对”。不过还是留意了一下Perl方面的书籍。《Perl入门》是一本小书,42块钱买来不值,于是我干脆发挥一下我在编程语言方面的功底,当场看完了事。

学习了更多编程语言原理方面的知识之后再来考察Perl语言果然有一些新感受。Perl关键字众多,但其实并不复杂:undef 好记还是 undefined?也没有 NULL 啦 nil 啦什么令人混淆的东西。空变量就是没定义,空表就是空表嘛。其它的关键字设定,如果你能忍受shell的fi什么的恶心东西那有什么理由不能忍受 Perl?

字符串用常量替换和括号代换体现是Perl的首创。学习前者的有PHP,后者有Ruby,不过弄来弄去还是不能取代Perl强大的上下文精神——到目前为止的语言都不能在字符串代换时展开列表甚至是展开列表切片就是一个证明。Ruby照抄了括号体系,但还是不敢抄Perl太多东西。在正则表达式里展开变量,IO描述符呢?它会和别的“纯”的语言一样露出叫你服从安排的嘴脸。

Perl的正则表达式已经红的发紫了——正如《学习Perl》一书骄傲的自夸——“很多语言和库都会以‘支持Perl风格正则表达式’作为卖点”。$` $& $' 这三个变量的设计更是叫我拍案叫绝!一方面,这三个变量对于实现不修改原数据的函数式编程来说是至关重要的,它可以把匹配过程表述为“过去,现在,将来”;另一方面,英语中常常使用`号作为应用开头以区别'x缩写形式,所以这种命名十分清晰,意思就是“开头,和,结尾”!(Perl使用的这种“命名推导”的体系和我早期设计的Mazy(t)是类似的:))

最后谈一点语义方面的东西。Perl的函数参数传递,本质上是只传递了一个变量(这与Ruby、Groovy之流的简单模仿具有核心上的不同),然后靠类似模式匹配的语法对它们进行命名。这个机制和我的Mazy (i)语言的做法是相同的;只不过我扩展了模式匹配的能力并把它提高到Lambda演算兼容的层次罢了。至于这种手法的优势——你不需要再去思考是用 max(a,b,c)还是max([a,b,c])甚至是apply(max,[a,b,c])了!Perl懒人们笑哈哈...由此可见,Python3 新加入的取代apply的语法,只能说明Guido大学课程没学好~~`


付:由Perl想到的Unix法则

不知道应该称为“法则”还是“通病”——概念充分实例化,尽可能地自圆其说。以Vim为例,普通模式,各种命令一套意思;换个环境或者开头控制符(这是Emacs吧?),命令不变,变成表达相反含义或对不同视角的对象应用合理解释后的命令;实在不能合理解释,怎么着也要凑一个上去。Perl也是这样。三种上下文标记,换成默认变量没法解释了,还要凑个类似的;散列怎么会有切片?《Perl入门》还强词夺理;while(<STDIN>) 这样的用法没意义,就凑一个方便的意义上去。不过还是那句话,“倒也门当户对”就是了。为了让大家能在看人打字手抽筋的时候安心喝茶,Larry Wall 用心良苦啊~`

June 24, 2007

FP 做数学统计题

今天布置的家庭作业还真是烦人!做完《数学之友》上“统计案例”一章所有习题。我的妈呀,算什么卡方、线性回归,公式繁琐地要命,按计算器都能把人按死。还是让电脑帮我做吧。
不用什么 GNUplot、Mathematica,俺用 Haskell,自己动手,丰衣足食!

一. 独立性检验
卡方就是一个公式:
  
kaf a b c d =
  ((sqr ((a * d) - (b * c))) * (a + b + c + d)) /
    ((a + b) * (c + d) * (a + c) * (d + b))

-- sqr 函数是用来求一个数的平方的,再实现一下:
sqr n = n * n
  
这样就搞定一种题了!以后算卡方不要太简单,把要检测独立性的四个数据按顺序作为 kaf 函数的参数输入 Hugs 就搞定了!
Main> kaf 184 61 91 9
11.097807845216 :: Double

二. 线性回归分析
其实就是求一个 y = a + bx 中的 a,b。把 xn 值、yn 值各表示为一个列表,像这样来使用:
Main> lrb [1..8] [5.54, 7.52, 10.02, 11.73, 15.69, 16.12, 16.98, 21.06]
2.12142857142857 :: Double
Main> lra [1..8] [5.54, 7.52, 10.02, 11.73, 15.69, 16.12, 16.98, 21.06]
3.53607142857143 :: Double
那么,就可以根据公式把它们抄出来:
  
lrb xl yl =
  -- 这里要注意一下,数学中的 sigma 是一种记法而非函数
  (sigma
   (\(x, y) -> (x - xp) * (y - yp)) (zip xl yl)) /
    sigma (\x -> sqr (x - xp)) xl
  where
    xp = avg xl
    yp = avg yl

lra xl yl = avg yl - (lrb xl yl) * avg xl
  
其中,avg 函数用来求一个列表的平均值(这里也就是所有 x 取值的平均值啦),它需要知道列表长度:
  
avg ls = sum ls / len ls

len [] = 0
len (x:xl) = (len xl) + 1
  
最后一个 sigma 函数的实现也很简单,就是累加嘛,只是写地有些不好看罢了:
  
sigma f (x:xl) =
  if xl == []
  then f x
  else f x + sigma f xl
  
重申一遍,这里的 sigma 被表示为函数而非数学形式。那么,猜猜下面的 sigma 的数学形式是什么?
Main> sigma (\(x, y) -> x * y) [(1,4), (5,3), (3,8), (2,4)]
51 :: Integer

三. 线性相关系数
线性相关系数 r 的求法也只是个函数,再抄一遍:
  
lrr xl yl =
    (sigma
   (\(x, y) -> (x - xp) * (y - yp)) (zip xl yl)) /
    sqrt ((sigma (\x -> sqr (x - xp)) xl) *
     (sigma (\y -> sqr (y - yp)) yl))
  where
    xp = avg xl
    yp = avg yl
  
以后这样用就行了:
Main> lrr [1..8] [5.54, 7.52, 10.02, 11.73, 15.69, 16.12, 16.98, 21.06]
0.987345979074916 :: Double
哇!这组数据的线性相关性接近 1,好高啊!

四. 结语
现在发现 Haskell 还真是实用,这个周末数学作业不愁了,打魔兽去喽!
最后给楼下的看客留一个与 FP 没什么关系的小题目:
对于一组线性相关数据,往往要求它的所有产生数据—— a、b、r,还有线性回归函数,顺便还要用这个函数再求一下对未来的预期。我如果当真输入 3 遍数据然后手工计算预期岂不是很傻?!想想看怎么让我只输入一遍数据就得到所有的产生数据(设计一下数据结构而已)还有线性回归函数(其实只有这个才有那么一点点技术含量)。

October 1, 2006

Scheme学习笔记(五)

(这应当是最后一篇了,明天来继续数组左旋...

八. 宏
  • MIT-Scheme的宏定义:
    (define-syntax 宏名
    (syntax-rules()
    ((模板) 操作))
    . . . ))
  • 在操作时,先用let绑定参数,然后用lambda定义过程:
    (let ((本地操作 (lambda 参数 宏主体 ...)))
    (lambda (e r)
    (apply 本地操作 (cdr e))))
  • (define-syntax start
    (syntax-rules ()
    ((start exp1)
    exp1)
    ((start exp1 exp2 ...)
    (let ((temp exp1)) (start exp2 ...))) ))
  • 实现的短小的定义:
    (define-macro MACRO-NAME
    (lambda MACRO-ARGS
    MACRO-BODY ...))
九. 结构
  • 结构模板定义:(defstruct 结构名 属性(不构成list))
(defstruct tree height girth age leaf-shape leaf-color)
  • 新建结构:(make-结构名 属性符号 值 符号 值)
(define coconut
(make-tree ’height 30

’leaf-shape ’frond
’age 5))
  • 使用结构:(结构名.属性名 结构对象);返回属性值,(set!结构名.属性名 结构对象 新属性值);更改属性值
(tree.leaf-shape coconut) => frond
(set!tree.height coconut 40)
(tree.height coconut) => 40
  • defstruct本身未提供,需要宏来定义,具体不再写出。
十. 面向对象
  • Scheme标准默认未提供!我看也用不着。
祝所有Sceme的爱好者们早日摆脱无聊和世俗的coder世界!

September 29, 2006

Scheme学习笔记(四)

(下一篇是最后一篇,讨论宏,结构和面向对象(其实没什么用))

四. 流程控制
  • Scheme中只有if操作是内置的,其它用宏实现。
  • 应尽量用尾递归代替循环。
  • (if (测试表达式) (操作) (else操作))
  • 由多个语句构成的操作用(begin)语句组合
  • (when (测试表达式) (多个操作))
  • (when (非测试表达式) (多个操作))
  • 其它的操作式不必用begin组合
  • case语句:
(case c
((#\a) 1)
((#\b) 2)
((#\c) 3)
(else 4)) => 3
  • 逻辑表达式操作:
  • and在无非比较时返回后一个值:(and 1 2) => 2
    (and #f 1) => #f
  • r在无非比较时返回前一个值:(or 1 2) => 1
    (or #f 1) => 1
六. 递归
  • 不能工作的代码,因为let或let*会将操作名绑定到各自的词法定界,导致互相递归调用的函数不能互访:
(let ((local-even? (lambda (n)
(if (= n 0) #t
(local-odd? (- n 1)))))
(local-odd? (lambda (n)
(if (= n 0) #f
(local-even? (- n 1))))))
  • 把绑定操作换成letrec即可。
  • 由begin引导的语句序列返回最后一个语句的值。
  • 命名let可简化局部递归调用:
(let countdown ((i 10))
(if (= i 0) ’liftoff
(begin
(display i)
(newline)
(countdown (- i 1))))) ;输出一个整数递减过程中的每个值
  • 迭代器(for-each 操作 (被操作list))
  • 迭代器(map 操作 (被操作list)),返回list中每一个被操作项构成的list(python里的那个函数的同名被复制品)。

September 28, 2006

Scheme学习笔记(三)

(另一些参考资料:Scheme语言介绍,语言概要()())

三. 过程
  • lambda形式:((lambda 参数 (过程)) 可选的实际参数)
((lambda (x) (+ x 2)) 5) => 7
  • 带括号时为形式参数,否则被认为是一个参数list
((lambda someFriends ; 参数周围没有括号 ! (DisplayLine "hi there " someFriends)) 'mole 'bear 'tiger)
  • 定义函数用define:(define add2 (lambda (x) (+ x 2))),省略lambda:(define (add6 x) (+ x 6))
  • 谓词(procedure?)来测试一个对象实际上是否为过程
  • 函数名即操作名:(add2 9) => 11
  • (apply 函数名 参数列表)强制调用。
  • 过程的套嵌定义遵循词法定界。
五. 词法定界(下篇讲“四”)
  • define定义当前定界以下对象。
  • (set!)仅改变当前定界中的对象:
(define counter 0)
(define bump-counter (lambda ()
(set! counter (+ counter 1))
counter))

(bump-counter) => 1
(bump-counter) => 2
(bump-counter) => 3
  • 相对define,(let (定义局部变量) (其它代码))绑定局部变量到过程:
(let ((x 2) (y 5)) (* x y)) => 10
  • let*内的定义不改变上级定界定义的局部变量
  • letrec的定义可互交引用?

(letrec ((even?
(lambda(x)
(if (= x 0) #t
(odd? (- x 1)))))
(odd?
(lambda(x)
(if (= x 0) #f
(even? (- x 1))))))
(even? 88)) => #t
  • letrec帮助局部过程实现递归。
  • 非标准的(fluid-let)不改变上级定界变量却引作当前定界局部变量——无聊。。。
(fluid-let ((counter 99))
(display (bump-counter)) (newline)
(display (bump-counter)) (newline)
(display (bump-counter)) (newline))
输出100,101,102,但原counter不变。

September 27, 2006

Scheme学习笔记(二)

(都忘了说了,我复习用的教材是Teach Yourself Scheme in Fixnum Days,还有一本The Scheme Programming Language也很好,但太长,没看过)

2. 复合类型

比较谓词:
(eqv?   (list 'a) (list 'a)) ; 它们 "看起来一样",
;()
(eq? (list 'a) (list 'a)) ; 存储在不同的内存单元中。
;()
(equal? (list 'a) (list 'a))
;#t
  • 字符串:申明(define str "This is a string.")
  • 用字符申明:(string #\h #\e #\l #\l #\o) => "hello"
  • (string-ref 字符串 位置(整数))返回字符串该位置上的字符
  • (string-append "E " "Pluribus " "Unum") => "E Pluribus Unum"
  • 改变字符串(重新赋值)(string-set!)
  • (string-length "mumble") => 6
  • 向量(貌似数组):值以"#"开头
  • 申明(vector 0 1 2 3 4) => #(0 1 2 3 4)或(make-vector 长度)
  • 对(不知道怎么翻译pairs这个词~~)
  • 分为左右值,左值叫car,右值叫cdr,用他们作操作符获取左右值
  • 申明(cons 左值 右值)
  • (cons 1 #t) => ’(1 . #t)
  • (set-car!),(set-cdr!)
  • 重要!经常右值套嵌!(cons 1 (cons 2 (cons 3 (cons 4 ’()))))形成list:(list 1 2 3 4)
  • list(太有名,懒得翻译)
  • list操作:取值(list-ref)
  • list-tail 通过除去前面的 n 个元素来获得一个子表
  • 添加元素(append)
  • (null?) 检查它的操作对象是否是空表。
  • 长度(length)
  • member 和 memq 将返回它的 car 是一个指定元素的一个子表。
  • 查找(assoc 'LittleBear '((HappyMole vampire) (LittleBear banshee) (LittleTiger troll))) => (LittleBear banshee)
  • 类型转换:(原类型-新类型 对象)
  • 类型转换方式非常自由:
(string->number "16") => 16
(symbol->string ’symbol) => "symbol"
尤其是这个:
(string->list "hello") => (#\h #\e #\l #\l #\o)

September 26, 2006

Scheme学习笔记(一)

(写这个文档并非我初学Scheme,只是为开始全面使用它作点复习准备)

一. HelloWorld
  • Scheme语法即抽象语法树,只有仅有的几个内部语法支持。
  • ;开启一行注释
  • 用括号进行文法定界,第一项为操作,空格(换行)分割操作对象,如:
(diplay "hello, world!")
即对"hello, world!"字符串进行 display 操作。
  • Scheme和Lisp一样是自描述的,脚本用 .scm 作为后缀,进入解释器后,使用(load "文件名字符串")启动程序,用(exit)退出解释器。
  • 完整程序:
(begin (display "hello, world!") (newline));newline用来刷新缓存

二. 数据类型
1. 简单类型
  • 布尔值:有两个取值,#t和#f
  • 操作(类型名? 变量)返回一个布尔值,确认该变量是否为此类型。如:
(boolean? #t) => #t
(boolean? 32) => #f
  • 宏(not 布尔值)返回这个布尔值的反取值。如:
(not (boolean? #t)) => #f
  • 数字:分为complex,rational,real,integer,但申明时不必指出。
  • (= 操作对象1 操作对象2)操作仅用于比较类型相同的变量值是否相同;(= 43 43) => #t,但(= 43 "ok")会报错。
  • (eqv?)操作是(=)的范型版本
  • 指数运算(expt 2 3) => 8,取绝对值(abs -7) => 7
  • 字符:以#\开头的单个字符
(char=? #\a #\a) ? #t
(char=? #\a #\b) ? #f
  • 忽略大小写比较:(char-ci=? #\a #\A) => #t
  • 大小写转换(不影响原值)(char-downcase #\A) => #\a,(char-upcase #\a) => #\A
  • 符号:编译原理中的东西,可作变量名,用(quote)申明,(quote a) => 'a
  • 变量不与其类型绑定:
(quote abc) (define xyz 9) (set! xyz #\c)