编程语言 - webdancer's Blog
Scala--基本类型和操作
1.类型:Scala的基本类型与Java中的Primitive type对应起来,不过都是首字母大写的类,例如Byte,Short,Int,Long,Char,Float,Double,String,Boolean
2.字面值(literal):在代码中直接写常数值的一种方式。这个Scala与Java基本相同。但是Scala提供Symbol字面值。
val s='type
3.操作:对于大部分操作符来说,与Java相同,但是“==”不同。在Scala中,"=="首先判断“==”左边是否是null,如果不是null然后条用equals方法。
4.Scala给基本类型提供了Rich Wrapper,可以有更加丰富的方法。
Scala学习-类,对象
1.类:类是对象的蓝图,根据类,使用new关键字,就可以创建对象了。和Java一样,Scala的对象也包含:属性和方法。这部分和Java基本上相同。
- 属性:默认是public的,使用private可以改变作用域;
-
方法:定义类的方法与定义普通的相同;Scala没有静态方法。
class ChecksumAccumulator { private var sum = 0 def add(b: Byte) { sum += b } def checksum(): Int = ~ (sum & 0xFF) + 1 }
在Scala的代码中,一个语句如果单独一行的话,可以不加分号,自动进行分号的推断;
- class可以带参数,class Rational(a:Int,b:Int)
- 使用override进行函数重载:
-
class Rational(a:Int,b:Int){ overrride def toString = a + "/" + b } - preconditions:可以使用require()。
class Rational(a:Int, b:Int){
require(b != 0)
val numer:Int = a
val demon:Int = b
override def toString = a + "/" + b
def add(that: Rational) = new Rational(
a * that.demon + b * that.numer,
b * that.demon
)
}
2.使用object定义singleton 对象。
import scala.collection.mutable.Map
object ChecksumAccumulator {
private val cache = Map[String, Int]()
def calculate(s: String): Int =
if (cache.contains(s))
cache(s)
else {
val acc = new ChecksumAccumulator
for (c <- s)
acc.add(c.toByte)
val cs = acc.checksum()
cache += (s -> cs)
cs
}
}
Singleton与class的名字相同,他们得放在同一源文件中,称为companion class 与companion object。不会创建新的类型;与class的名字不同,称为standalone对象,这种对象可以作为一个Scala程序的启动点。
3.Scala程序的启动类似Java,使用含有main方法的Singleton对象。
运行程序的话,首先使用scalac或是fsc进行编译,然后使用scala运行(不含后缀),终于Java的命令行是类似的。
也可是使用Application traits来启动程序,不过这种方法仅仅用在一些简单的,单线程程序中。
Scala学习-语言基础
1.Scala解释器:键入scala命令启动,使用和python的差不多;也可以运行脚本文件:
scala xx.scala
2.变量定义:两种变量类型:val和var,val类型变量一旦初始化,不能再被赋值,相当于java中的final类型的变量。与java不同的是Scala可以进行类型推导,不用显示的写出变量的类型。
var a = "Hello World" val b = 3.14
如同想要显示的写出变量类型,可以在变量名称后面加上“:type”:
var a:String = "Hello World" val b:Int = 3.14
3.函数定义:
scala的函数定义用"def"作为开始的标识符,
def max(a:Int, b:Int):Int = {
if (a>b)
a
else
b
}

需要注意:函数的返回值在非递归函数时可以省去,但是作为良好的编程习惯,还是加上;在只有一个语句的情况下,大括号可以省略。
def error = println("Error!")
Scala的函数参数传递方式一般是CBV(call by value),如果参数类型前面有“=>”则使用CBN(call by name)。
4.控制结构:可以用while进行循环,if进行判断,与java一样的。
5.使用foreach和for来进行迭代:
函数时first citizen,可以充当参数,所以foreach的参数可以是function literal。例如如果输出脚本的参数,
args.foreach((arg:String) => println(arg))

也可以使用for,
for(arg <- args)
println(arg)
python的namespace问题
1.问题
写程序的时候,出现了一个bug,看了很久也没有发现问题。程序的逻辑模样大致如下所示:
for i in range(4):
x = [ e**i for i, e in enumerate(range(5))]
y = 2 * i
print y
如果对python很熟悉的,一眼就可以看出问题的所在。程序的原意是:y = 2 * i 这条语句中i与第一行中的i一致的,但是却使用了第二行中的i值,但是程序依然循环了四次。打印的结果是:
8 8 8 8
这令人非常的疑惑,主要是在python 2,x中,list comprehension中的变量的scope没有仅限于‘[ ]’中,而是漏了出来。这的确有点不太符合逻辑,因为在理解上,‘[ ]’中的变量的scope仅限于里面,更加符合直观的感觉。
2.解决
解决的方法:
- 避免名字冲突就行了,比如把第二行的i换成j。
- 升级python的版本。在python 3.x中,这个问题就不存在了,list comprehension表达式有了自己的scope.(是不是应该升级到python 3了??但是好像一些包还是支持3.x不太好呀,囧。。。)
3.python 的namespace和scope
namespace就是一个从名字到对象的映射,现在的python好像就是用字典数据结构实现的。对于namespace注意的就是生命周期(lifetime)和作用域(scope)。所谓生命周期就是实现namespace的对象在内存中存在的时间,而作用域(scope)就是namespace在程序中起作用的文本区域。
理解过程抽象
在学习编程的过程中,一个核心的任务是编写可以完成我们任务的函数。不管我们使用的什么语言,做什么开发,函数抽象并不少见。所谓的函数,我认为就是一些语句的集合,完成一个特定的任务。有了函数,我们思维时的操作原子型的层次变的比较高,思维的难度就会降低。但是在不同的编程实践中,为了完成任务需要的编写函数的思维方式也许是不同的,这会受到编程语言或是第三方库的影响。
#include<stdio.h>
void swap_int(int * a, int * b){
int tmp=0;
tmp=*a;
*a=*b;
*b=tmp;
}
void bubblesort(int * a,int n){
int i;
int j;
for(i=0;i<n-1;i++){
for(j=n-1;j>i;j--){
if (a[j]<a[j-1]){
swap_int(&a[j],&a[j-1]);
}
}
}
}
def bubblesort(l):
n=len(l)
for i in range(n-1):
for j in range(n-1,i,-1):
if l[j]<l[j-1]:
l[j],l[j-1]=l[j-1],l[j]
public class BubbleSort{
public static void sort(int[] l){
int n=l.length;
for(int i=0;i<n;i++){
for(int j=n-1;j>i;j--){
if(l[j]<l[j-1]){
swap_int(l,j,j-1);
}
}
}
}
private static void swap_int(int[] l,int m,int n){
int tmp=l[m];
l[m]=l[n];
l[n]=tmp;
}
}
(define (bubblesort l)
(let ((n (vector-length l)))
(map (lambda (i)
(map (lambda (j)
(if (< (vector-ref l j) (vector-ref l (- j 1)))
(swap l j (- j 1))))
(range (- n 1) i -1)))
(range 0 n 1))))
(define (swap l a b)
(let ((tmp (vector-ref l a)))
(vector-set! l a (vector-ref l b))
(vector-set! l b tmp)))
(define (range low high step)
(if (or (and (> step 0) (> low (- high 1))) (and (< step 0) (< low (+ high 1))))
()
(cons low (range (+ low step) high step))))
(define l (vector 2 3 1 5 4 7 8 9 1))
(bubblesort l)
(display l)
- 过程抽象对于我们解决问题,非常有用。使用高级语言提供的机制,可以大大解放我们的编程效率。
- 理解问题的时候,通常没法子一下理解的很好,所以在对问题的理解上,要多花功夫。
- 在写过程式的语言时,对于一些地方很模糊,这就是我们训练的地方。
- 函数式的编程方式,对思维的训练量很大。
python中迭代器和生成器
l=[1,2,3,4]
for n in l:
print n
在看上面这段代码的时候,我们没有显式的控制列表的偏移量,就可以自动的遍历了整个列表对象。那么for 语句是怎么来遍历列表l的呢?要回答这个问题,我们必须首先来看一下迭代器相关的知识。
1.迭代器
迭代器对象要求支持迭代器协议,所谓支持迭代器协议就是对象包含__iter__()和next()方法。其中__iter__()方法返回迭代器对象自己;next()方法返回下一个前进到下一个结果,在结尾时引发StopIteration异常。
列表不是迭代器对象,但是列表通过__iter__()可以得到一个迭代器对象来遍历整个列表的内容,像列表这样的序列对象都属于这种情况;与序列不同,文件对象本身就是一种迭代器对象。
l=[1,2,3,4]
f=open('test.c','r')
iter(l) == l
Out[131]: False
iter (f)== f
Out[132]: True
一个迭代器的例子(来源:python tutorial)
class Reverse:
"""Iterator for looping over a sequence backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
2.生成器
生成器使python可以很容易的支持迭代协议。生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yeild一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议。
一个生成器的例子(来源:python tutorial)
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
3.for语句如何工作
在我们最前面的遍历列表的for语句中,for使用了列表支持迭代器的性质,可以每次通过调用迭代器的next()方法,来遍历到列表中的值,直到遇到StopIteration的异常。
4.注意的问题:
- 像列表这种序列类型的对象,我们可以通过iter()来产生多个迭代器,在迭代的过程中各个迭代器相互对立;但是迭代器对象没法通过iter()方法来产生多个不同的迭代器,它们都指向了自身,所以没法独立使用。
参考: python tutorial, stackoverflow
python的字典类型
前几天复习了hash的相关知识,正好在做udacity的CS101课程用到了python里面的字典,正好复习一下python的字典知识。
1.字典类型基础
python中的字典(dictionary)是一种映射类型,它不同于列表(list)这样的序列类型,它不是以偏移来存取,而是以键来存储,所以字典不支持切片这样的列表操作。
键的类型可以是数字,字符串,不包含可变对象的元组,不能使列表。字典可以被看做一个无序的集合。
常见操作:
1.新建字典;2.添加(键,值)对;3删除(键,值)对;4.由键查找对应的值;5.键,值上循环。
>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
注意问题:序列运算无效;对新键赋值会创建新的项;键值的类型可以不同;避免keyError。
2.为什么列表不能做键
python使用开放散列数据结构实现的。不管是用列表的id还是用列表的内容用来散列,都是符合散列函数的定义的(不同的散列值,不同的键值)。但是这两种方式都存在一些问题:
- 用id散列时,在用不同的键值(id不同),但是内容相同的列表查找时,尽管散列值不同,但是在用列表却相同(满足__equal__)。此外,还有就是列表时一个容器,如果用id散列,意义到底有多大呢?
- 用列表内容散列时,列表内容该更改后,我们会计算出不同的散列值,就会有不同的bucket。
总之,在python中,选择了列表不能做键的策略。我感觉就是既然tuple类型可以用内容来散列,那么我们就不用列表了,列表修改特性会使散列的维护变得很复杂(想想如果我们使用列表的内容散列了,而后我们修改了,我们自己都不知道以前是什么了?如果想要保存以前的内容,不是又做了copy,这样就不如直接用tuple类型了),从而使得这种常用的数据类型的性能出现问题。
注意:用户自定义类型都可以用作键。只要用对象id散列,比较函数用id进行比较。为什么在列表中不可以,但是在自定义类型中就行?一个主要的原因就是对放在字典中的自定义类型来说,id重要,有了id就可以找到内容了。
#转帖#成为Python高手
本文是从 How to become a proficient Python programmer 这篇文章翻译而来。
- 函数式编程
- 性能
- 测试
- 编码规范