CS107 Programming Paradigms (Stanford University) by Jerry Cain (22-27)学习笔记

Lecture22

写一个名为power_set的函数, 它将生成包含输入集合所有子集的集合.

例:

> ( power_set  '(1  2  3) )

( () (2) (3) (2 3)  (1) (1 2) (1 3) (1 2 3))    // 注意: 是组合, 不是排列.

以上结果可以看成是由两部分组成:cons  '(() (2) (3) (2 3))  '((1) (1 2) (1 3) (1 2 3))

另 外:

> (power_set  '())

(())

定义:

(define (ps  set) 

    ( if (null?set)  '(())

          ( append ( ps (cdr  set ) )               //  1

               ( map ( lambda ( subset)

                   ( cons (car  set)  subset ) )  //  lambda中的函数用于map 操作时, 对列表中每个元素进行运算.

                   ( ps ( cdr set ) ) ) ) ) )              //  2  本行产生map操作的列表

以上代码中, 1/2两处的set指向同一个集合对象  Scheme中通过变量名来向对象是唯一的方法.

//--------------------------------------------------------

写递归函数的技巧:

A. 最简值中抽出规则:1. 尾元素+ 剩余元素   2. 列出几个头元素的具体生成

B. 给出最简时的值:初值. 采用A中获得的递归规则进行叠加.

C. 写递归函数时往往是从给定的n开始,反向递减.

在Scheme语言中, 没有循环, 一般用递归函数来代替循环.

//--------------------------------------------------------

( define  ( ps  set ) 

     ( if ( null ? set) '(()) 

        ( let ( ( ps_rest ( ps ( cdr set ) ) ) )     // 使得只作一次cdr运算, 运算结果可以在let 范围内各处使用, 从而节省时间. 

            ( append  ps_rest

                   ( map ( lambda ( subset) 

                        ( cons ( car  set)  subset ) )

                         ps_rest  ) ) ) ) )

此定义与上定义等价,但运行效率高许多.

//--------------------------------------------------------

let 表达式:

( let ( ( x  expv1)

          ( y  expv2) 

          ( z  expv3) ) 

   ... )

实际上:

( let ( ( x ...)

          ( y ...) )

   ( a  x   y ) )

等价于:

(( lambda ( x  y )

      ( a   x   y )) ...   ...)

let 是换一种不同的方式将一些应用函数表示成一些有待评估的表达式.

//------------------------------------------------------------------------

写一个名为permate 的函数.它能输出列表的全部6种排列.

> (permate '( 1  2  3))

( ( 1 2 3 )  ( 1 3 2 )    // 以1 打头

  ( 2 1 3 )  ( 2 3 1)   // 以2打头

  ( 3 1 2 )  ( 3 2 1) ) // 以3打头

接收一个长度为n的列表,输出的是一个长度为 n! 的列表.

( define ( permate items)

  ( if ( null?items ) '(())

     ( apply  append

                ( map ( lambda (elem) 

                     ( map ( lambda ( permatation )

                           ( cons  elem permatation ) )

                           ( permatation ( remove items elem ) ) ) )

                   items ) ) 

//------------------------------------------------

Scheme程序编写技巧:

其程序可看作由一部分一部分拼起来,在写的过程中,可以不按程序运算的逻辑顺序来写.( 可以先写后面的再写前面的, 先写中间的再写两头的等等. )

Scheme的许多内置函数后面都维护着一个数据结构,因此用户只需要调用内置函数,而无需自已再根据应用来分配、调度和管理内存;无需用户自已来维护相应的数据结构。

Scheme的程序运行的执行过程,可以分为三个步骤:输入-评估-打印.

不论用户输入的数据是什么类型,Scheme都会在后台产生一个链表,接收输入的数据后将其填写到链表相应的结点中,并标明数据类型,最后返回这个链表的首地址,为后面的评估过程作准备。

> '(1 2 3)

( 1 2 3 )

> ( cons 1 ( cons 2 ( cons 3 '() ) ) )

( 1 2 3 )

以上两段代码是等价的。

cons作为Scheme的一个符号是依附在一段代码上的,解释器很熟悉,解释器遇到cons后就知道怎么分配一段内存给相关的数据,分配好后,它还要知道在各已分配内存空间中需要放入什么数据。


以上为Scheme的基本工作原理,可以用C++模拟写一个min版的Scheme解释器。



Lecture23

Scheme的内存分配和管理

> ( define  seq  '(1 2 3) )     // 此定义将名为seq的变量与结点值域分别为1, 2, 3的链表关联起来. seq 为一个全局变量.

> ( car  seq )  // car 评估并获取全局变量seq 所指身的链表的首结点的数据域的值.

> ( cons '(1 2 3) '(4 5 6) )

((1 2 3 ) 4 5 6)

在cons运算之前, 系统会生成两个链表,一个链表的结点分别为: 1  2  3. 另一个链表的结点分别为 : 4 5 6. cons运算后, 会生成一个新结点. 结点的值域保存链表 1 2 3 的首地址, 结点的指针域保存链表 4  5  6 的首地址.


> ( cons '(1 2)  '(1 2) )                                   // 1

   ( ( lambda (x) ( cons  x  x ) )  '( 1  2 ) )        // 2

由于打印函数对内存管理视若无睹. 打印函数只简单地要求打印列表的car部分和cdr部分. 所以以上两个函数的打印结果是一样的. 但实际上,两个函数的内存分布是不一样的.第二个函数产生的只有三个结点的链表,第二和第三个结点分别保存值1  2. 而首结点的值域和指针域同时指向第二结点.


//--------------------------------------------------------------


> ( map car '( (1 2) (3 4) (5 6 7) ) )  // map 可带任意个参数

(1  3  5)

> ( map + '(1 2) '(10 20) '(100 400) )

(111  422)

> ( map * '(1)  '(2)  '(3)  '(4)  '(5) )

(120)

实现:

( define ( unary-map  fn seq ) )    // 比较Lecture21中的my_unary_map

    ( if ( null ? seq) ()

         ( cons ( fn (car seq) ) 

                   ( unary-map fn ( cdr seq ) ) ) ) )


因为: 

( define ( bar  a  b  c . d) 

        ( list  a  b  c  d ) )

> ( bar 1 2 3 4 5 6 )

( 1 2 3 ( 4 5 6 ))

> ( bar 1 2 3 )

( 1 2 3 ())

也就是说点"." 表示其后无论有多少个参数, 都将被收集起来放到参数列表中.

所以map的定义为: 

( define ( map  fn  first-list  other-list) 

    ( if ( null ? first-list) '()

       ( cons ( apply fn

                     ( cons ( car first-list ) 

                         ( unary-map car  other-list ) ) ) 

                 ( apply map

                     ( cons fn

                         ( cons ( cdr first-list )

                             ( unary-map  cdr  other-list ) ) ) ) ) ) )


map 底层是依靠cons来帮助它分配空间的. cons调用时Scheme会自动生成一个cons区域.

当打印输出完成后, 之前cons分配得到的cons内存区域就会被回收.

例子:

>( define x '(1 2 3) )   // 变量x与链表( 1  2  3 )关联起来了.

>( define y (cdr x) )

>( define x '(4 5) )   //  这样一来结点结点 1  就无变量指向了.  某些系统会给每个结点设一个计数器, 当计数器为0时, 由垃圾回收器自动回收.

Scheme的垃圾回收机制:

所有变量(如: x, y)放在Symbal map集中, 系统自动生成的链表结点放在mast cons set集中, 每个单元结点都可以被单独释放. 然后根据define的定义将变量与链表结点关联起来.

系统会根据算法, 在需要的时候对Symbal map中的所有变量作一次搜索. 对mast cons set中与之有关联的结点作上标记. 然后令mast cons set收回其集中所有未做标记的结点.

//-----------------------------------------------------------------

ML 是Scheme的增强版

Harkll

Erlang 优势: 并行处理




Lecture24
C语言是由写unix的人发明的, 目的是使写操作系统更容易. 
C++在70年代末~80年代初开始出现.
Python从2000年开始流行.
循环的一轮被称为一次迭代, 所谓用迭代来代替递归, 就是用循环来代替递归. 反过来, 递归也可以用来替换迭代. 如scheme语言所示.
"hello there".statswith("...")
"hello there".capitalize()
"Hello There".istitle()
"[ ]" 表列表且此列表可变.
//--------------------------------
smallnum = [ 1, 2, 3, 4, 5, 6]
len( smallnum )
smallnum[ -2 ]
smallnum[ len( smallnum ) - 1 ]
smallnum[ 1 : 3 ]
smallnum[ 0 : 3 ]
smallnum[ 0 : 0 ]
smallnum[ -2 : ]
smallnum[ 1 : -1 ]
smallnum[ 1 : 0 ]
smallnum[ 1 : ]
"hello there"[ 4 : 7 ]
//--------------------------------
seq = [ 0, 1, 2, 3, 5, 6, 7, 8]
seq[ 4 : 4 ] = [ 4 ]
seq
//--------------------------------
seq = [ 5, 9, 2, 0 ]
seq
seq[ 0 ] = 14
seq
//--------------------------------
seq = ( 1, 2, 3, 4 )
seq[ 1 ] = 14
//--------------------------------
seq = [ "a", "b", "c"]
seq.append( "d" )
seq
//----------------------------------------------
在名为 divisors.py 的文件中作如下定义:
def  getherDivisors( number ) :
divisors = []
for  div  in  range( 1,  number+1 ) :
if  number % div  == 0:
divisors.append( div )
return divisors
每一行的缩进靠制表符设定.
三重引号对表注释字符串将出现在多行中.
在其它模块中引用函数getherDivisors的方式有两种.
方法1:
>>> import  divisors
>>> divisors.getherDivisors( 24 )
方法2:
>>> from divisors import getherDivisors
>>> getherDivisors( 24 )
//--------------------------------------------------
字典
>>> student = {}  // 大括号对描述一个字典常量的所有内容.
>>> student
{}
>>> student["name"] = "Linda"
...
>>> student["gpa"] = 3.56
>>> student
...
>>> student["gpa"]
...
>>> student["gpa"] = 4.5
...
Python中的所有对象( 如: 列表, 字典等 )都是通过引用传递的.
>>> student["friends"] = ["aa", "bb", "cc"]
>>> student
...
>>> student["ideas"] = {}
>>> student
...
>>> playground = { }
>>> playground[ 4 ] = "think"
>>> playground
...



Lecture25

字典是Python的基础

grammar = { '<start>' : [ [ 'This', '<object>', 'is here.' ] ],

                        '<object>' : [ [ 'computer' ], 

                                              [ 'car' ],

                                              [ 'assignment' ] ] };

在Scheme中可以用序列化的对象结构来表达数据.

本例中:

    grammar在内存中的字典有两个key分别是 start 和 object. 它们各自分别映射到一个list的列表.  start对应的列表长度为1, object对应的列表长度为3.


import  sys

from random import choice,  seed

def  expand( symbol )

if  symbol.startswith( '<' ):   //  '<' 表尖括号

definitions = grammar[ symbol ]

expansion = choice( definitions )   //  实际上以一个整数或一个列表作为参数, choice是内置函数, 用以获得随机数. 若输入n, 则choice返回一个介于0与n之间的随机数                                                                                 //  若输入一个列表, 则choice等概率地随机选取一个此列表的元素, 并返回此元素.

map( expand, expansion )     //  用递归而非迭代的方法来得到所有值.

else

sys.out.write(symbol)

运行:

>>> seed()

>>> expand( '<start>' )


若将grammar作为参数则上例可改为

def  expand( symbol, grammar )

if  symbol.startswith( '<' ):  

definitions = grammar[ symbol ]

expansion = choice( definitions )   

map( expand, expansion )     // map 只能映射带一个参数的一元函数. 

                                                                    //  lambdas 实际上在Python中也存在, 此句也可表达为:

                                                                        map( lambdas, item: expand ( item,  grammar ),  expansion )

else

sys.out.write(symbol)

运行:

>>> seed()

>>> expand( '<start>', grammar )

//---------------------------------------------------------------

Python中的各种对象实际上是通过引用( 别名 )被四处传递的.
>>> x = [1, 2, 3]
>>> x
[1, 2, 3]
>>> y = x
>>> y
[1, 2, 3]
>>> x.append(4)
>>> x
[1, 2, 3, 4]
>>> y
[1, 2, 3, 4]
>>> z = [10, 12, 14]
>>> z
[10, 12, 14]
>>> w = [z, z]
>>> w
[ [10, 12, 14],  [10, 12, 14] ]
>>> z .append( 17 )
>>> z
[10, 12, 14, 17]
>>> w
[[10, 12, 14, 17], [[10, 12, 14, 17]]


若不想通过引用, 而对赋值变量分配新的内存空间, 则需要使用浅拷贝或深度copy贝.
from  copy  import  copy,  deepcopy
>>> x = [14, 15, 21]
>>> x
[14, 15, 21]
>>> y = x
>>> y
[14, 15, 21]
>>> x is y    // is 为内置
True
>>> z = copy( x )    //  只进行浅拷贝( 一层拷贝 )
>>> z
[14, 15, 21]
>>> z is x
False


>>> m = [ 1, 2, 3 ]    //  内存中生成三个结点组成的链表, 链表首地址赋给m, 结点的值域中的值分别为1, 2, 3.
>>> n = [m,  m]     //  内存中生成两个结点组成的链表, 链表首地址赋给n,  两个结点的值域都指向链表m 
>>> p = deepcopy( n )  //


//------------------------------------------------------------


Python 中的类和对象.
Python中的类和对象的实现都是以字典为基础的. 从内存的角度看, Python中类与字典的实现机制是一样的. Python, Object-C等语言都是把类( 或结构 )中的field作为字符串储存在字典中的.
lex.py 文件:
class  lexicon:
def  __init__( self,  filename = 'words' ) :  // self 是从Object-C中" 借来"的
infile = open( filename, 'r' )
words = infile.readlines()
self.words = [ ]                          // Python对象的内存最初被初始化为空, 当调用此self.words = ... 时就会为其中添加一个名为words的成员. 这一点与C/C++等在语言是不                                                                          同的后者在为对象分配完内存空间后, 此空间包含哪些成员变量, 它们又是如何分布的就已经确定了.
for  word  in words :
length = len( word ) - 1
self.words.append( word[ : length ] )


def  ContainsWord( self,  word ) :
return  self.words[ bisect( self.words, word ) - 1 ] == word
Python实际类对象通过一个字典来建模, C/C++的所有事情都是在编译时完成的.
使用:
>>> from lex import lexicon
>>> el = lexicon()
>>> lexicon.__dict__   // 此句可以得到与lexicon相对应的字典内存分布说明. 其含有所有嵌入在内部的符号的列表.
>>> el.words = [ ]     // Python 无"私有成员变量" 或 "私有成员函数" 的概念.  此句清空了lexicon类对象el的self.world成员变量, 此成员变量在__init__函数中曾被初始化.
>>> el.__dict__
[ 'words' : [ ] ] 
//-------------------------------------------------------------

>>> o = object()   // Python将自动产生一个Object对象
>>> o
<...>
>>> o.__dict__
{ }
>>> o.a = 17                         //  o.__dict__[ 'a' ] = 17
>>> o.b = "hello"                  //  o.__dict__[ 'b' ] = "hello"
>>> o.c = False                   //  o.__dict__[ 'c' ] = False
>>> o.__dict__
{ 'a':17, 'b':"hello",  'c':False }
Python所使用的对象是动态可扩展的, C/C++ , Java的所有任何代码其对象大小在对象一产生后就已经确定了.
Python是一种动态执行可扩展语言, 在C/C++中函数调用要借助Activation Record, 但Python则无此机制.




Lecture26

Python的XML解析与Internet编程.

Python是较"年轻"的语言, 所以它内置了一些Internet编程所需的函数.

对XML的解析有两种方式: 

1. 基于流的方式: 不将全部信息完整地存储到内存中,在任何时候只存储整个文本流的一个子集.

课程中自定义XML文件结构:

<title> ... </title>

    <channel>

        <item>

            <title> Article Title </title>

            ...

        </item>

        <item>

           ...

        </item>

    </channel>


from  urllib2  import urlopen

from  xml.sax  import  make.parser

import  sys

def  ListFeedTitles( url ) :

infile  =  urlopen( url ) :

parser = make.parser()

parset.setContentHandler( RSSHandler() )

parser.parse( infile )



class ContentHandler:

def  __init__(self) :

ContentHandler.__init__(self)

self.__inItem = False

self.__inTitle = False           //  变量前加双下划线,表此变量为类成员变量.Python中类的私有变量前必须强制加双下划线,可以在类内部直接引用但在类外部引用会                 //  出错.


def  Characters(sellf, data) :

if  self.__inTitle:

sys.stdout.write( data )


def  startElement( self, tag, attrs ) :

if  tag == "item" :  self.__inItem = True

if  tag == "title"  and  self.__inItem :

self.__inTitle = True


def  endElement( self, tag )

if  tag == "title"  and  self.__inTitle :

sys.stdout.write("\n")

self.__inTitle = False

if  tag == "item"

self.__inItem = False


// 上例中的startElement与endElement主要作用是维护__inTitle和__inItem两个成员变量.


2. 称为SAX方法

将XML的文档数据整个完整地存储到内存中.( XML文档整个是一个树型结构 )

因此,可以在内存中对XML的相关数据进行修改、增删。

这样一来,可以实现网页浏览时的交互操作:比如,购物网站上用户点击按钮将物品装入购物篮中等。




Lecture27

Haskell是与Scheme类似的函数式编程语言.  官网:http://www.haskell.org/haskellwiki/Haskell

从2003年出现开始, 逐渐由一种研究型语言走向实际开发应用.

与Scheme一样, 函数式编程不考虑机器如何来执行, 执行顺序如何, 而给出了问题求解的数学公式就可以直接由此得到全部的程序.

与Scheme一样, 函数可以作为一个类型在程序中传递. ( 编程原本中将函数作为类型来考虑, 它们之间是否有共通的理论基础? )

与Scheme不一样, Haskell有类型检测.

与Python不一样, Haskell不是面向对象的.

Haskell在定义用户自已的数据类型时, 用的形式有点象南大出版的《编译原理》(张幸儿)中 介绍的语义表达方法。 






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值