编程语言 - 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.解决

解决的方法:

  1. 避免名字冲突就行了,比如把第二行的i换成j。
  2. 升级python的版本。在python 3.x中,这个问题就不存在了,list comprehension表达式有了自己的scope.(是不是应该升级到python 3了??但是好像一些包还是支持3.x不太好呀,囧。。。)

3.python 的namespace和scope

namespace就是一个从名字到对象的映射,现在的python好像就是用字典数据结构实现的。对于namespace注意的就是生命周期(lifetime)和作用域(scope)。所谓生命周期就是实现namespace的对象在内存中存在的时间,而作用域(scope)就是namespace在程序中起作用的文本区域。

理解过程抽象

在学习编程的过程中,一个核心的任务是编写可以完成我们任务的函数。不管我们使用的什么语言,做什么开发,函数抽象并不少见。所谓的函数,我认为就是一些语句的集合,完成一个特定的任务。有了函数,我们思维时的操作原子型的层次变的比较高,思维的难度就会降低。但是在不同的编程实践中,为了完成任务需要的编写函数的思维方式也许是不同的,这会受到编程语言或是第三方库的影响。

在面向对象编程中,想得是如何构建类以及如何设计类之间的关系,充分利用对象的特性,例如在使用Java编程中,会想着如何设计Class,使用什么样的设计模式,这时候函数抽象的层次依赖与我们的类设计。在使用C这样的编程语言时,使用面向对象的设计,还是直接使用函数用来模块化,也会带来不同的设计函数的思维方式。使用像Lisp这样的函数式编程语言时,我们的思维与过程式的编程语言的方式可能会有很大的不同,函数就处于核心地位了。编程的思维可能会受到我们学习过的很多东西的影响,正如上面我陈述的。我的经验是学会抽象和使用语言提供的高层抽象,这样会大大的减低我们抽象函数的难度,使我们的注意力集中到解决我们的问题。通过自己的抽象和语言提供的高层抽象来实现层次化。
 
下面我使用不同的编程语言实现了冒泡排序,体会一下编程语言对我们思维的影响。
对冒泡排序,我还是比较熟悉的。算法的基本思想就是:两个指针i,j,i控制每次冒泡一遍的bubble的位置,j控制每一边冒泡时要比较的数的位置,两次循环就行了。
 
C语言实现
#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]);
                }
            }
        }
    }
在写这段代码时,我饭的错误就是给`swap_int`传递实参的时候没有取地址,这就是我对`C`语言的思维的盲区,对于`a[j]`与`a+j`我没有他们不同的认识。
 
python实现
    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]
显然`python`要比C短的多,但这不代表我没有出错,因为有一段时间没写代码了,所以对`range(a,b)`的区间表示方式到底用了`[a,b]`还是`[a,b)`已经有点模糊不清了,所以就出了错。当然这种基本的问题,我们应该很明确的,但是尽管如此,我还是出错了。
 
java实现
    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;
        }
    }
尽管`java`代码已经很久没写了,但是`java`与`c`的过程太相似了,基本没有费功夫,这里就是感觉java只有传值的参数传递方式的原因,`swap`函数一点也不优雅。
 
scheme实现
    (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)
写`scheme`代码,因为最近在看`SICP`,所以代码的错误只是笔误,变量名写错这样的原因,可是我还是没能使用函数式的编程方式来写这段代码,主要就是函数式编程就要使用辅助的空间,所以使用了赋值操作。
 
从上面的过程中我有这么几个总结:
  1. 过程抽象对于我们解决问题,非常有用。使用高级语言提供的机制,可以大大解放我们的编程效率。
  2. 理解问题的时候,通常没法子一下理解的很好,所以在对问题的理解上,要多花功夫。
  3. 在写过程式的语言时,对于一些地方很模糊,这就是我们训练的地方。
  4. 函数式的编程方式,对思维的训练量很大。
思维是一个逐渐完善的过程,我们不能期望它一下子满足期待,要循序渐进的推进。要不断的训练,并且反馈,也许我们才会有一个比较好的抽象函数的能力,不管是面向对象编程还是函数式编程。

 

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.注意的问题:

  1. 像列表这种序列类型的对象,我们可以通过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还是用列表的内容用来散列,都是符合散列函数的定义的(不同的散列值,不同的键值)。但是这两种方式都存在一些问题:

  1. 用id散列时,在用不同的键值(id不同),但是内容相同的列表查找时,尽管散列值不同,但是在用列表却相同(满足__equal__)。此外,还有就是列表时一个容器,如果用id散列,意义到底有多大呢?
  2. 用列表内容散列时,列表内容该更改后,我们会计算出不同的散列值,就会有不同的bucket。

总之,在python中,选择了列表不能做键的策略。我感觉就是既然tuple类型可以用内容来散列,那么我们就不用列表了,列表修改特性会使散列的维护变得很复杂(想想如果我们使用列表的内容散列了,而后我们修改了,我们自己都不知道以前是什么了?如果想要保存以前的内容,不是又做了copy,这样就不如直接用tuple类型了),从而使得这种常用的数据类型的性能出现问题。

注意:用户自定义类型都可以用作键。只要用对象id散列,比较函数用id进行比较。为什么在列表中不可以,但是在自定义类型中就行?一个主要的原因就是对放在字典中的自定义类型来说,id重要,有了id就可以找到内容了。

#转帖#成为Python高手

本文是从 How to become a proficient Python programmer 这篇文章翻译而来。

这篇文章主要是对我收集的一些文章的摘要。因为已经有很多比我有才华的人写出了大量关于如何成为优秀Python程序员的好文章。
 
我的总结主要集中在四个基本题目上:函数式编程,性能,测试,编码规范。如果一个程序员能将这四个方面的内容知识都吸收消化,那他/她不管怎样都会有巨大的收获。
 
  • 函数式编程
命令式的编程风格已经成为事实上的标准。命令式编程的程序是由一些描述状态转变的语句组成。虽然有时候这种编程方式十分的有效,但有时也不尽如此(比如复杂性)——而且,相对于声明式编程方式,它可能会显得不是很直观。
 
如果你不明白我究竟是在说什么,这很正常。这里有一些文章能让你脑袋开窍。但你要注意,这些文章有点像《骇客帝国》里的红色药丸——一旦你尝试过了函数式编程,你就永远不会回头了。
 
http://www.amk.ca/python/writing/functional
http://www.secnetix.de/olli/Python/lambda_functions.hawk
http://docs.python.org/howto/functional.html
 
  • 性能
你会看到有如此多的讨论都在批评这些“脚本语言”(Python,Ruby)是如何的性能低下,可是你却经常的容易忽略这样的事实:是程序员使用的算法导致了程序这样拙劣的表现。
 
这里有一些非常好的文章,能让你知道Python的运行时性能表现的细节详情,你会发现,通过这些精炼而且有趣的语言,你也能写出高性能的应用 程序。而且,当你的老板质疑Python的性能时,你别忘了告诉他,这世界上第二大的搜索引擎就是用Python写成的——它叫做Youtube(参考Python摘录)
 
http://jaynes.colorado.edu/PythonIdioms.html
http://wiki.python.org/moin/PythonSpeed/PerformanceTips
 
  • 测试
如今在计算机科学界,测试可能是一个最让人不知所措的主题了。有些程序员能真正的理解它,十分重视TDD(测试驱动开发)和它的后继者 BDD(行为驱动开发)。而另外一些根本不接受,认为这是浪费时间。那么,我现在将告诉你:如果你不曾开始使用TDD/BDD,那你错过了很多最好的东 西!
 
这并不只是说引入了一种技术,可以替换你的公司里那种通过愚蠢的手工点击测试应用程序的原始发布管理制度,更重要的是,它是一种能够让你深入理 解你自己的业务领域的工具——真正的你需要的、你想要的攻克问题、处理问题的方式。如果你还没有这样做,请试一下。下面的这些文章将会给你一些提示:
 
http://www.oreillynet.com/lpt/a/5463
http://www.oreillynet.com/lpt/a/5584
http://wiki.cacr.caltech.edu/danse/index.php/Unit_testing_and_Integration_testing
http://docs.python.org/library/unittest.html
 
  • 编码规范
并非所有的代码生来平等。有些代码可以被另外的任何一个好的程序员读懂和修改。但有些却只能被读,而且只能被代码的原始作者修改——而且这也只是在他或她写出了这代码的几小时内可以。为什么会这样?因为没有经过代码测试(上面说的)和缺乏正确的编程规范。
 
下面的文章给你描述了一个最小的应该遵守的规范合集。如果按照这些指导原则,你将能编写出更简洁和漂亮的代码。作为附加效应,你的程序会变得可读性更好,更容易的被你和任何其他人修改。
 
http://www.python.org/dev/peps/pep-0008/
http://www.fantascienza.net/leonardo/ar/python_best_practices.html
 
那就去传阅这这些资料吧。从坐在你身边的人开始。也许在下一次程序员沙龙或编程大会的时候,也已经成为一名Python编程高手了!
 
祝你学习旅途顺利。
 
如果你喜欢这些文章,请在微博上顶一下,让其他人也知道。来自: 外刊IT评论
 
注:本文从丕子学长blog看到的,他的博客里面内容很丰富,大家可以看一下
 




Host by is-Programmer.com | Power by Chito 1.3.3 beta | © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee