程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> CS107 Programming Paradigms (Stanford University) by Jerry C

CS107 Programming Paradigms (Stanford University) by Jerry C

編輯:C++入門知識

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 = { '' : [ [ 'This', '', 'is here.' ] ],

'' : [ [ '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( '' )


若將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( '', 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文件結構:

...

Article Title

...

...


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在定義用戶自已的數據類型時, 用的形式有點象南大出版的《編譯原理》(張幸兒)中 介紹的語義表達方法。






  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved