我是靠谱客的博主 无心水蜜桃,最近开发中收集的这篇文章主要介绍漫谈顺序、分支和循环,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

今天讲课,将顺序、分支和循环这三种流程结构聊了聊。本文作为泛读资料(有问题请跟帖提问)。

3种流程

在狄杰斯特拉(Edsger W. Dijkstra)反复研究面条式代码(spaghetti code),并在1968年给某位编辑写了一篇著名的简信,题为《Go to语句是有害的》之后,计算机科学家Corrado Bohm和Giuseppe Jacopini证明,使用顺序(sequencing),分支/选择(alternation)和循环(iteration)这三种流程结构就足以表达所有程序的本质。三种流程结构如同万花筒中的彩纸,构成无穷无尽极富变化的程序。早期的编程书籍中,会有顺序程序设计,选择结构程序设计和循环结构程序设计等说法。有时候,人们将它们称为控制抽象(control flow abstraction)。随着变量、表达式和控制语句被所有的现代语言支持,人们已经习以为常地不将它们作为抽象或抽象机制了,仅将它们作为语句或程序的实现看待。

三种流程结构的进一步说明,顺序结构参见[0.2.2命令编程范式],分支结构参见[1.2.5策略模式(5.9)],循环结构参见[0.2.3函数编程范式]。下面综合的说明一下。

1.顺序结构(命令范式Vs.函数范式)

一江春水向东流。在命令编程范式如C语言中,将编程过程定义为一个命令/指令序列的开发,这些指令操作数据而改变机器的状态,程序则由一系列的语句组成。因此,其程序一般有这样的形式[1]:

statement1;
statement2;
……

但是,函数编程范式中,一个个函数解决小问题,组合起来解决大问题。因此Scheme仅对一些有副作用的表达式(命令),Scheme以begin封装一系列的表达式,顺序执行。
(begin (display "Hello") (newline) )

其中:

  • display命令,输出它的参数即字符串。
  • newline命令,输出一个换行。

2.分支结构(OO中)

虽然C与Scheme有些差异,总之都是分情况进行不同处理(参考循环中的例子)。但是,如果有人在OO中不假思索地选择分支结构,通常是垃圾程序员。多态本身就是根据具体对象的不同执行不同的行为,是天然的分情况进行不同处理。在面向对象程序中,通常程序员会自然地选择多态,分支结构通常较少使用。面向对象程序员在遇到少量需求变化时,可能会通过分支结构简单应付;如果需求变化较多时,通常会以多态重构其分支结构

3.循环(迭代Vs.递归)

【我把这部分内容放在  0.2.3函数编程范式 中】C/Java语言教学中,会讲循环/迭代语句;而Scheme会介绍递归。Java也可以递归,如下:

    static long sum1(long n) {
        return (n == 1)? 1:(n + sum1(n - 1));
    }

为什么纯函数式编程语言如Scheme要用递归而且能够用递归呢

首先,纯函数式编程语言不喜欢变量的值不断地变更,数据的不变性是他们的宗教信仰。例如for(int i=0;i < n;i++)中,i总在变,转一圈++一下。对于Scheme而言,这是违反其基本价值观的异端,因此Scheme中不采用迭代结构如while、for。

其次,递归,更一般地,函数的调用,都有创建新的栈帧/stack frame的开销。Java程序的递归,由于JVM为一个Java程序预备的内存有限,大量地递归调用会出现栈溢出错误。如果函数对另外一个函数的调用是尾调用,解释器可以通过尾调用优化/tail-call optimization避免帧的创建,所以Scheme中,如果递归函数是尾递归,事实上是迭代执行的(SICP1.2.1中比较含混地说明了这一点)。命令式语言/C语言程序员最喜欢赋值语句,所以程序员使用循环/迭代语句非常自然和爽快,以至于C、Java编译器迟迟不愿意添加尾调用优化——没有动力。而使用Scheme语言编写尾递归,就能够等价于Java的while、for等迭代结构。

当然,如果Scheme使用普通的递归(SICP中称其为“线性递归过程”),也会和Java递归一样,导致栈溢出错误。

栈溢出

Scheme需要通过尾递归达到Java使用for语句的效果(SICP中称其为“迭代计算过程”)。简单的尾递归和使用状态变量的尾递归均可。

(define (sum n)
  (if (= n 1)
      1
      (+ n (sum (- n 1)))))
;;;(sum 5000000) ;;;栈溢出

;;;尾递归
(define (sum-iter acc n)
  (if (= n 1)
      acc
      (sum-iter (+ acc n) (- n 1))))

(define (sum n) (sum-iter 1 n))
(sum 5000000)

;;;使用状态变量
(define (sum-iter total i max)
  (if (> i max)
      total
      (sum-iter (+ total i)
                (+ i 1)
                max)))

(define (sum n) (sum-iter 0 1 n))
(sum 5000000)

总之,循环语句是采用迭代还是递归,源于一门编程语言的基本价值观(和尾调用优化支持)。顺便提醒:关于循环,对它的理解分三个层面:①基本实现:迭代 vs递归,②迭代器,③流。


[1]这些特点在Java的方法实现中保留,因此Java是命令型的面向对象语言。

最后

以上就是无心水蜜桃为你收集整理的漫谈顺序、分支和循环的全部内容,希望文章能够帮你解决漫谈顺序、分支和循环所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(66)

评论列表共有 0 条评论

立即
投稿
返回
顶部