【自我介绍】
各位朋友,大家好,我是 Merily 博士。我毕业于中国科学院,留学于美国卡耐基研究所,后来又在美国顶尖的索尔克生物研究所从事博士后研究,我研究的主要方向是细胞生物学,胚胎发育学等领域,现在我在美国的一家医学中心担任辅助受孕顾问,帮助患者如何实现更好的孕育。
【课程的目的】
因为工作和生活的原因,我经常会接触到很多美国家庭,深刻地感觉到由于中美文化差异和信仰的不同,美国人的生育观念跟咱们中国人还是很有区别的,比如说,一般美国人并没有「养儿防老」这个概念,也没有「不孝有三,无后为大」这样的舆论压力;整体来讲,妇女在家庭中的地位也还是比较平等的。但就生男孩儿还是生女孩儿这件事,咱们不妨来猜一下,美国人到底有没有诉求呢?
按照我们的理解,美国人应该不在意孩子性别吧,对这个应该是很随意、很开放的吧?但是实际上,美国也有相当一部分家庭,对生男生女有诉求,他们特别渴望得到一定性别的孩子,或者是男孩儿,或者是女孩儿,尤其已经有一个孩子或者多个孩子的家庭,对性别选择的要求就更加强烈。
但是他们的出发点,更多的只是单纯的喜欢男孩或者女孩,或者想要儿女双全,跟咱们中国的传统宗族观念下的生男生女完全不一样。但是不管怎么样,其实中国人和美国人,对生男生女这个事情,都还是有强烈意愿的。于是我在工作和生活中,也开始留心观察和研究,到底怎么才能生男生女,能不能在怀孕之前就想办法控制?而不是像以前那样,怀孕之后要通过流产才能来选择性别。因为从医学和伦理学的角度来看,全世界没有任何一个国家,会积极鼓励和提倡用有创性的手段来进行性别选择,尤其是流产。
所以我们要研究和观察的是,怎么样能让大家在自然受孕的过程中,采用一定的方法和技巧,或者遵循一定的规律,来最大可能的获得我们理想中想要性别的孩子,我想这对任何一个家庭都将是一件很美好的事情。
幸运的是科学家和生殖医学专家,确实发现了一些受孕过程中的有趣现象,通过对这些现象的观察和研究,他们也总结了一些方法,可以帮助大家大大提高生男或者生女的机率。这些方法不需要服用药物,不需要做有创性的检查,甚至有时候只需要一个体温计和一张纸、一只笔就够了。根据调查和回访,采用这些方法生男的成功率可以达到 80-90%,生女的成功率可以达到 70-80%。当然,尽管这个数据可能会有「幸存者偏差效应」,但是如果您极度渴望生男孩或者生女孩,这些方法还是值得尝试的。
这节课里,我把这些方法总结出来,教给大家,希望大家能够最大可能的得到自己理想性别的宝宝。当然,实现这个方法的前提是,一定要先了解生男生女的原理,以及人体受孕的过程。所以在这套课程的第二节和第三节,我会重点讲一下这些内容。您会发现,其实生男生女跟大家以前知道的可能不太一样,并不是简单的 X 精子和 Y 精子就决定了生男生女。包括人体的受孕过程,也有很多地方比想象的要神奇的多,正是基于这些奇妙的原理和现象,我们才能总结出相对有效的生男生女方法。在第四节里,我要教大家一些准备工作,这些准备工作非常重要,是生男生女能不能成功的关键和重要前提。在您做了充分的准备工作之后,就是最激动人心的实操课了。在第五节和第六节里,我会分别告诉您生男的方法和生女的方法,希望大家能够熟练掌握,大大提高生男生女的机率,早日实现自己的家庭梦想。
# 从流程图到程序:用 Pytho 编写顺序查找算法
前面学了一大堆变量啊,赋值啊,到底有什么用呢?
马上用处就来了——
## 修改顺序查找流程图——用变量和赋值替换其中的自然语言
### 以前的流程图
还记得我们之前已经学习了顺序查找算法吧(还记得吗)?
当时我们是画了一张流程图。先来回顾一下:

***流程图-1***
在这种图里,我们其实已经使用了数组(列表)变量 arr,和整型变量 i,只不过当时我们还不知道它们是**变量**,而仅仅将其当作数组和数组下标的形式化写法而已。
### 重画流程图
通过前面两章的学习我们知道,上图中大部分关于 arr 和 i 的操作就是对数组和整型变量赋值的。
在 Pytho 中我们用列表来实现数组操作,其实此处 arr 是列表类型的变量。而 le(arr)则是 Pytho 的一个内置函数,是用来计算列表长度的。
那么我们是不是可以利用已经学过的 Pytho 语言的要素,将上图中余下的自然语言文字也替换掉,都换成赋值语句或者内置函数的调用操作呢?
我们来试试:

***流程图-2***
在程序开始的时候,先初始化两个变量:t——整型,目标数(target umer)和 arr——列表型,存放数列的数组。
然后给 i 赋初值 0,之后进入循环,循环的准入条件是 i 小于 arr 的长度——即元素个数。
在循环内部,将 t 和 arr[i]比较,如果相等则打印出对应元素的索引,然后退出循环(reak 表示退出当前循环),否则 i 递增 1 之后继续循环。
一旦到了 i 的值和 arr 的元素个数相等,就说明整个 arr 里面所有的元素都与 t 比较过,且都不相等,此时打印出「failed」告知用户查找失败了。
果然可以哦!
大家发现没有,上面这张重新画的流程图里,每一个矩形或者菱形框里面的内容,都变成了几乎可以直接写在程序中的语句。
因此,只要有办法把流程图中的控制结构写成程序语句,整个算法的实现就有了!
怎么在程序中体现顺序、条件、循环三种不同的结构呢?
## 关键字与控制结构
### 关键字
在介绍具体控制结构的写法之前,我们需要先了解一个概念——关键字!
**关键字(Keyword)**,也叫保留字(Reerved Word),是编程语言中的一类**语法结构**。
基本每一种编程语言都会有一系列关键字,它们在语言的格式说明里被预先定义,通常具有特殊的语法意义。
这些意义中非常重要的内容就是用来识别代码块、函数和流程控制结构。
### Pytho 的关键字
Pytho2 和 Pytho3 的关键字大体相同,既然用的是 Pytho3 我们只看 Pytho3 就好了。
其实,我们可以通过 Pytho 语句直接获得当前版本的关键字,具体代码如下:
imort keyword
rit()
这么简单的代码,我们直接在命令行运行就好了:

我们可以看到,Pytho3 的关键字总共有 33 个。这些关键字当然各有用处。
关于 Pytho 关键字的详解,网上到处都有,大家感兴趣可以自己搜索。
不过,作者认为,现在我们就是把每一个的作用都详细说一遍,你也记不住。不如先简单浏览一下,用到什么再具体学什么。
下面是从使用角度,对这 33 个关键字做的简单分类:
- 布尔类型字面量:
True
Fale
- 表示「不存在」的特殊型字面量:
Noe
- **关于流程控制**:
if
elif
ele
for
while
reak
cotiue
- 关于逻辑判断:
ad
or
ot
- 关于函数:
def
retur
yield
lamda
- 关于类型判断:
i
- 关于面向对象编程:
cla
a
- 关于代码块控制:
with ... a
- 关于 Lit 操作:
del
i
- 关于变量作用域:
gloal
olocal
- 关于异常处理:
aert
excet
fially
raie
try
- 关于导入模块:
from
imort
目前我们马上要用到的,就是控制流程相关的几个关键字。也就是 if,elif,ele,for,while,reak,cotiue。其中 if,ele 和 while 是最最基础的流程控制关键字。
我们现在就来看看,用这几个关键字,怎么构造各种流程控制结构。
### Pytho 中的三种控制结构
下面这几幅图就是直观的转换方法:
#### 顺序结构
顺序结构非常简单,实现它都不需要关键字,直接把各个步骤的代码按照从前到后的顺序罗列出来即可。

***顺序结构***
下面是一个顺序结构的例子:
legth = 10
width = 5
ace = legth * width
rit("Sace i: % quare meter" % ace)
***代码-1***
输出为:
> Sace i: 50 quare meter
套用我们之前学过的代码块的概念,在这段代码里,四行代码都属于同一个代码块,四行代码地位均等:

因此,它们要运行,就一条不差地按从上到下的顺序运行。
#### 条件结构
大多数情况下,条件结构需要用到 2 个关键字:if 和 ele;有时只需要 if 就可以;还有的时候,另需要 elif 关键字。
我们先来看看大多数的情况:

***条件结构***
if 关键字是条件结构必须有的关键字,if 标志了一个控制结构的开始。if 之后是一个空格,然后是条件语句的条件——对应流程图中菱形框内的部分。
> **NOTE**:代码中的条件部分其实不必用括号括起来,图中这么画是为了让你看清楚条件。
if 语句的结尾是一个**冒号**——这个冒号是必须有的,初学者经常会忽视这个冒号,结果导致程序报错却不知道错在哪里,这一点要注意。
在 if 行之下,有一个子代码块,这个代码块的内容对应流程图中 YES 之后的部分。
而 NO 对应的部分,需要用另一个关键字:ele 标识出来,ele 后面只有一个冒号。ele 行下面是原本属于 NO 分支的子代码块。**子代码块**需要***一级缩进***,即后退 4 个空格。
下面是一段示例代码:
if ace > 30:
rit("Thi i a ig room.")
ele:
rit("Thi i a ormal room.")
***代码-2***
> **NOTE**:这段代码实际上延续代码-1 的,在代码-1 中 ace 已经被赋值了,所以此处无须再赋值。
>
> 这里例子里每个子代码块都只有一个语句,如果有多个语句的话,这些语句全部相对于「if」一级缩进,它们互相之间是对齐的。
输出是:
> Thi i a ig room.
从代码块的角度解释,在这段代码里面,if 所在一行和 ele 所在一行仍然属于 Block1,但打印「ig room」的语句和打印「ormal room」的语句就成了 if 语句和 ele 语句的子代码块。结构如下:

Block2-1 由 if 语句控制,到了 if 语句时,Pytho 解释器要检查一下变量 ace 的值是否大于 30。如果是,则进入 Block2-1 执行;否则则跳过 Block2-1。
if 语句及其子代码块整个运行过后(无论 Block2-1 是否真的被执行了),ele 语句解释器会检查一下 ace > 30 条件的逆条件,即 ace <= 30,如果符合这个逆条件,则进入 Block2-2,否则跳过 Block2-2。
如果 NO 分支无内容,也可以完全没有 ele 行及其子代码块。例如这样的代码:
if ace > 30:
rit("What a ig room!")
***代码-3***
也就是只有 ace > 30 时才打印,否则就什么都不打印。
----------
如果有超过两种情况要考虑,则可以用 elif 语句,elif 实际上就是「ele if」。比如下面例子:
if ace > 30:
rit("Thi i a ig room.")
elif ace > 10:
rit("Thi i a ormal room.")
ele:
rit("Thi i a mall room.")
***代码-4***
代码-4 的细节如下:
- if 语句:
ace > 30 时输出「Thi i a ig room.」
- elif 语句:
ace > 30 条件不满足的情况下,也就是在 ace <=30 的时候,再检查是否 ace > 10,如果是(也就是 10 < ace <= 30) 的时候,输出"Thi i a ormal room."
- ele 语句:
本语句的隐含条件是前面所有条件所限定的情况的并集的逆。
就是说到了 ele 这条语句,就说名当前 ace 变量的值,对于之前的(1)ace > 30 或者 (2)10 < ace <= 30 这两个条件都不满足。也就是说此时的状况是:ace <= 10,针对这种情况,输出"Thi i a mall room."
#### 循环结构
循环结构对应的关键字是 while 或者 for。
在开始学的时候,我们先关注尽量少的关键字,暂时只用 while 来构造循环。实际上所有用 for 构造的循环都可以用 while 重写。
先来看最简单的情况:

***循环结构-1***
循环结构以 while 关键字开始,while 语句格式类似 if 语句,也是 while+空格+循环条件+冒号。
while 行下也是一个子代码块,对应流程图中形成环路的 YES 分支。在 YES 分支完成后,while 对应代码块也就结束了。
如果 NO 分支如果有内容的话,将 NO 分支的部分直接放在 while 所控制的整段代码后面。不必再回退 4 格。
下面是一个例子:
arr = ["ale", "orage", "watermelo"]
i = 0
while i < le(arr):
rit(arr[i])
i = i + 1
rit("No fruit.")
***代码-5***
输出为:
> ale
> orage
> watermelo
> No fruit.
这段代码的代码块结构如下:

Block2 由 while 语句控制。代码执行到 while 语句时,先判断当时变量 i 的值是否小于 arr Lit 的长度,如果满足条件,则进入 Block2 执行其中的两条代码。
Block2 是 while 循环的循环体。每次 Block2 执行完之后,都要再跳回到 while 语句,判断 while 的循环条件 —— i < le(arr) 是否成立。如果成立,则再进入循环体,否则跳过 Block2,继续后面的 Block1。
一旦 Block2 被跳过,说明循环条件的逆条件已经成立了。比如上面的代码,一旦 i 的值不再符合 i < le(arr),则说明已经是 i >= le(arr) 的情况了。
----------
有的时候循环体中会用到 reak 或 cotiue 关键字,前者用于忽然终止循环跳到循环之外,后者则是跳过当前这轮循环,继续进入下一轮循环。
cotiue 一般影响不大,就是会让循环体少执行一次。但是如果遇到 reak 要注意,在循环体中一旦遇到 reak 语句,就直接跳出,而且不会再回头执行 while 中的条件判断。
这个时候,如果要保持原图中循环条件的 NO 分支在确定循环条件为否的时候执行,就要在 while 循环之后,再加一重判断。用图画出来就是:

***循环结构-2*** (其中「!」符号表示取反的意思)
此种结构的例子:
arr = ["ale", "orage", "watermelo"]
i = 0
while i < le(arr):
rit(arr[i])
if (arr[i] == "orage"):
reak
i = i + 1
if i == le(arr):
rit("No fruit.")
***代码-6***
输出是:
> ale
> orage
#### 不同类型结构的嵌套
不同类型的控制结构是可以嵌套的,比如代码-6 中,其实循环结构的循环体中就已经嵌套了一个条件结构了。
嵌套没有更多的限制,只要注意:
- 每个被嵌入到其他结构中的结构都是一个完整的结构体。
- 可以是简化的
例如:条件结构只有 if,没有 elif,也没有 ele —— 简化的结构体
- 但不能是残缺的
例如:条件结构有 if 和 elif,却没有 ele ——残缺的结构体
- 把每个被嵌入的结构都当作一个语句来对待。
##用 Pytho 实现顺序查找算法
### 代码实现
我们按照刚才学的不同结构类型的代码表达,来实现一下流程图-2 中描述的顺序查找算法。代码如下:
***代码-7***
输出为:
> t i elemet 11
### 流程图和代码的对照
流程图和代码的对应关系如下:

这个算法有两个大块结构:顺序(浅蓝色框内)结构和循环结构(浅灰色框内)。
顺序结构自不必言,循环结构看一下代码的实现:
1. 循环条件放在一个 while 语句里,符合循环条件的 YES 分支是是 while 分支下的子代码块(棕色框内);
2. 棕色框内部,也就是 while 循环的子代码块内,正好又有一个条件分支,这个条件分支的 YES 分支中包含了 reak 语句;
3. 与循环条件相悖的 NO 分支放在 while 语句之后,因为循环体中包含 reak 语句,因此循环条件的 NO 分支要再用一个 if 语句来判断是否当时条件正好和 while 中的条件相反(求反)。
