Full Code of DeathKing/Learning-SICP for AI

master 211bbe61b9de cached
135 files
10.2 MB
2.7M tokens
14 symbols
1 requests
Copy disabled (too large) Download .txt
Showing preview only (10,724K chars total). Download the full file to get everything.
Repository: DeathKing/Learning-SICP
Branch: master
Commit: 211bbe61b9de
Files: 135
Total size: 10.2 MB

Directory structure:
gitextract_b_q_3yll/

├── .gitignore
├── Ass/
│   ├── lec10a.chn+eng.ass
│   ├── lec10a.chn.ass
│   ├── lec10a.eng.ass
│   ├── lec10b.chn+eng.ass
│   ├── lec10b.chn.ass
│   ├── lec10b.eng.ass
│   ├── lec1a.chn+eng.ass
│   ├── lec1a.chn.ass
│   ├── lec1a.eng.ass
│   ├── lec3a.chn+eng.ass
│   ├── lec3a.chn.ass
│   ├── lec3a.eng.ass
│   ├── lec3b.chn+eng.ass
│   ├── lec3b.chn.ass
│   ├── lec3b.eng.ass
│   ├── lec4a.chn+eng.ass
│   ├── lec4a.chn.ass
│   ├── lec4a.eng.ass
│   ├── lec4b.chn+eng.ass
│   ├── lec4b.chn.ass
│   ├── lec4b.eng.ass
│   ├── lec5a.chn+eng.ass
│   ├── lec5a.chn.ass
│   ├── lec5a.eng.ass
│   ├── lec5b.chn+eng.ass
│   ├── lec5b.chn.ass
│   ├── lec5b.eng.ass
│   ├── lec6a.chn+eng.ass
│   ├── lec6a.chn.ass
│   ├── lec6a.eng.ass
│   ├── lec6b.chn+eng.ass
│   ├── lec6b.chn.ass
│   ├── lec6b.eng.ass
│   ├── lec7a.chn+eng.ass
│   ├── lec7a.chn.ass
│   ├── lec7a.eng.ass
│   ├── lec7b.chn+eng.ass
│   ├── lec7b.chn.ass
│   ├── lec7b.eng.ass
│   ├── lec8a.chn+eng.ass
│   ├── lec8a.chn.ass
│   ├── lec8a.eng.ass
│   ├── lec8b.chn+eng.ass
│   ├── lec8b.chn.ass
│   ├── lec8b.eng.ass
│   ├── lec9a.chn+eng.ass
│   ├── lec9a.chn.ass
│   ├── lec9a.eng.ass
│   ├── lec9b.chn+eng.ass
│   ├── lec9b.chn.ass
│   └── lec9b.eng.ass
├── Preface/
│   ├── pre1.txt
│   ├── pre2.txt
│   ├── pre3.txt
│   ├── pre4.txt
│   └── pre5.txt
├── README.md
├── SrtCN/
│   ├── lec10a.eng.srt
│   ├── lec10a.srt
│   ├── lec10b.eng.srt
│   ├── lec10b.srt
│   ├── lec1a.srt
│   ├── lec1b.srt
│   ├── lec2a.srt
│   ├── lec2b.srt
│   ├── lec3a.srt
│   ├── lec3b.srt
│   ├── lec4a.srt
│   ├── lec4b.srt
│   ├── lec5a.srt
│   ├── lec5b.srt
│   ├── lec6a.srt
│   ├── lec6b.srt
│   ├── lec7a.srt
│   ├── lec7b.srt
│   ├── lec8a.chn.srt
│   ├── lec8a.srt
│   ├── lec8b.eng.srt
│   ├── lec8b.srt
│   ├── lec9a.srt
│   ├── lec9b.eng.srt
│   └── lec9b.srt
├── SrtEN/
│   ├── lec10a_512kb.mp4.srt
│   ├── lec10b_512kb.mp4.srt
│   ├── lec1a_512kb.mp4.srt
│   ├── lec1b_512kb.mp4.srt
│   ├── lec2a_512kb.mp4.srt
│   ├── lec2b_512kb.mp4.srt
│   ├── lec3a_512kb.mp4.srt
│   ├── lec3b_512kb.mp4.srt
│   ├── lec4a_512kb.mp4.srt
│   ├── lec4b_512kb.mp4.srt
│   ├── lec5a_512kb.mp4.srt
│   ├── lec5b_512kb.mp4.srt
│   ├── lec6a_512kb.mp4.srt
│   ├── lec6b_512kb.mp4.srt
│   ├── lec7a_512kb.mp4.srt
│   ├── lec7b_512kb.mp4.srt
│   ├── lec8a_512kb.mp4.srt
│   ├── lec8b_512kb.mp4.srt
│   ├── lec9a_512kb.mp4.srt
│   └── lec9b_512kb.mp4.srt
├── Sub/
│   ├── lec10a.txt
│   ├── lec10b.txt
│   ├── lec1a.txt
│   ├── lec1b.txt
│   ├── lec2a.txt
│   ├── lec2b.txt
│   ├── lec3a.txt
│   ├── lec3b.txt
│   ├── lec4a.txt
│   ├── lec4b.txt
│   ├── lec5a.txt
│   ├── lec5b.txt
│   ├── lec6a.txt
│   ├── lec6b.txt
│   ├── lec7a.txt
│   ├── lec7b.txt
│   ├── lec8a.txt
│   ├── lec8b.txt
│   ├── lec9a.txt
│   └── lec9b.txt
└── Tools/
    ├── con2table.rb
    ├── contributor.json
    ├── download-sicp-movies.sh
    ├── lec.json
    ├── lec2tabel.rb
    ├── merge.rb
    ├── pdf2txt.rb
    ├── separate.rb
    ├── split.pl
    ├── split.rb
    ├── timeline.rb
    └── util.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.*~
#*#
.DS_Store
Thumbs.db
*.backup
Tools/*.srt
Tools/*.ass


================================================
FILE: Ass/lec10a.chn+eng.ass
================================================
[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.601
PlayResX: 640
PlayResY: 480

[Aegisub Project Garbage]
Active Line: 1691
Video Position: 197

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: EN,Calisto MT,21,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,1,0,2,10,10,30,1
Style: Declare,微软雅黑,30,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,8,10,10,10,1
Style: staff,微软雅黑,30,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,0,2,5,10,10,10,1
Style: title,微软雅黑,35,&H001D64D9,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,0,1,5,10,10,10,1
Style: Default,雅黑宋体,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,1,0,2,10,10,30,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:19.36,0:00:22.65,EN,,0,0,0,,PROFESSOR: Last time, we took a look at
Dialogue: 0,0:00:22.65,0:00:25.67,EN,,0,0,0,,an explicit control evaluator for Lisp
Dialogue: 0,0:00:25.67,0:00:28.97,EN,,0,0,0,,and that bridged the gap between all these high-level languages
Dialogue: 0,0:00:29.05,0:00:32.14,EN,,0,0,0,,like Lisp and query language all that stuff
Dialogue: 0,0:00:32.50,0:00:36.16,EN,,0,0,0,,bridged the gap between that and a conventional register machine.
Dialogue: 0,0:00:36.70,0:00:40.14,EN,,0,0,0,,And in fact, you can think of the explicit control evaluator
Dialogue: 0,0:00:40.16,0:00:44.38,EN,,0,0,0,,either as, say the code for a Lisp interpreter
Dialogue: 0,0:00:44.40,0:00:45.95,EN,,0,0,0,,if you wanted to implement it in the
Dialogue: 0,0:00:46.52,0:00:49.50,EN,,0,0,0,,assembly language of some conventional register transfer machine,
Dialogue: 0,0:00:49.50,0:00:51.50,EN,,0,0,0,,or, if you like, you can think of it as the microcode
Dialogue: 0,0:00:52.08,0:00:54.56,EN,,0,0,0,,of some machine that's going to be specially designed to run Lisp.
Dialogue: 0,0:00:55.20,0:00:55.92,EN,,0,0,0,,In either case,
Dialogue: 0,0:00:55.92,0:00:58.68,EN,,0,0,0,,Nwhat we're doing is we're taking a machine
Dialogue: 0,0:00:58.94,0:01:00.51,EN,,0,0,0,,that speaks some low-level language
Dialogue: 0,0:01:01.42,0:01:03.32,EN,,0,0,0,,and we're raising the machine
Dialogue: 0,0:01:03.37,0:01:04.88,EN,,0,0,0,,to a high-level language like Lisp
Dialogue: 0,0:01:05.36,0:01:06.35,EN,,0,0,0,,by writing an interpreter.
Dialogue: 0,0:01:08.22,0:01:09.58,EN,,0,0,0,,So for instance
Dialogue: 0,0:01:11.82,0:01:13.77,EN,,0,0,0,,here, conceptually,
Dialogue: 0,0:01:18.01,0:01:19.47,EN,,0,0,0,,here conceptually is a
Dialogue: 0,0:01:20.54,0:01:23.44,EN,,0,0,0,,a special purpose machine for computing factorials.
Dialogue: 0,0:01:24.09,0:01:27.39,EN,,0,0,0,,It takes in five and puts out 120.
Dialogue: 0,0:01:28.92,0:01:30.83,EN,,0,0,0,,And what this special purpose machine is
Dialogue: 0,0:01:30.97,0:01:32.72,EN,,0,0,0,,actually a Lisp interpreter
Dialogue: 0,0:01:33.50,0:01:36.17,EN,,0,0,0,,that's configured itself to run factorials
Dialogue: 0,0:01:38.35,0:01:40.99,EN,,0,0,0,,because you feed into it a description of the factorial machine.
Dialogue: 0,0:01:42.12,0:01:43.70,EN,,0,0,0,,So that's what an interpreter is.
Dialogue: 0,0:01:43.70,0:01:45.66,EN,,0,0,0,,It configures itself to
Dialogue: 0,0:01:46.37,0:01:49.24,EN,,0,0,0,,emulate a machine whose description you read in.
Dialogue: 0,0:01:50.07,0:01:51.93,EN,,0,0,0,,Now, inside the Lisp interpreter, what's that?
Dialogue: 0,0:01:52.04,0:01:55.44,EN,,0,0,0,,Well, that might be your general register language interpreter
Dialogue: 0,0:01:56.98,0:02:00.18,EN,,0,0,0,,that configures itself to behave like a Lisp interpreter
Dialogue: 0,0:02:00.18,0:02:02.03,EN,,0,0,0,,because you put in a whole bunch of instructions
Dialogue: 0,0:02:02.12,0:02:03.04,EN,,0,0,0,,in register language.
Dialogue: 0,0:02:03.37,0:02:05.16,EN,,0,0,0,,This is the explicit control evaluator.
Dialogue: 0,0:02:07.05,0:02:08.70,EN,,0,0,0,,And then it also has some sort of library
Dialogue: 0,0:02:08.73,0:02:11.08,EN,,0,0,0,,a library of primitive operators and Lisp operations
Dialogue: 0,0:02:11.12,0:02:12.28,EN,,0,0,0,,all sorts of things like that.
Dialogue: 0,0:02:12.75,0:02:16.89,EN,,0,0,0,,That's the general strategy of interpretation.
Dialogue: 0,0:02:17.32,0:02:18.51,EN,,0,0,0,,And the point is, what we're doing
Dialogue: 0,0:02:18.60,0:02:20.14,EN,,0,0,0,,is we're writing an interpreter
Dialogue: 0,0:02:21.62,0:02:23.40,EN,,0,0,0,,to raise the machine
Dialogue: 0,0:02:23.42,0:02:25.24,EN,,0,0,0,,to the level of the programs that we want to write.
Dialogue: 0,0:02:25.24,0:02:26.72,EN,,0,0,0,,Well, there's another strategy
Dialogue: 0,0:02:27.42,0:02:28.89,EN,,0,0,0,,a different one, which is compilation.
Dialogue: 0,0:02:29.04,0:02:30.43,EN,,0,0,0,,Compilation's a little bit different.
Dialogue: 0,0:02:31.04,0:02:31.50,EN,,0,0,0,,Here--
Dialogue: 0,0:02:33.37,0:02:34.75,EN,,0,0,0,,here we might have produced
Dialogue: 0,0:02:35.67,0:02:38.52,EN,,0,0,0,,a special purpose machine for,
Dialogue: 0,0:02:38.62,0:02:39.98,EN,,0,0,0,,for computing factorials
Dialogue: 0,0:02:43.62,0:02:46.26,EN,,0,0,0,,starting with some sort of machine that speaks register language
Dialogue: 0,0:02:46.26,0:02:47.72,EN,,0,0,0,,except we're going to do a different strategy.
Dialogue: 0,0:02:47.72,0:02:50.38,EN,,0,0,0,,We take our factorial program.
Dialogue: 0,0:02:51.55,0:02:53.92,EN,,0,0,0,,We use that as the source code into a compiler.
Dialogue: 0,0:02:53.92,0:02:55.15,EN,,0,0,0,,What the compiler will do
Dialogue: 0,0:02:55.15,0:02:57.62,EN,,0,0,0,,is translate that factorial program
Dialogue: 0,0:02:57.62,0:02:59.07,EN,,0,0,0,,into some register machine language.
Dialogue: 0,0:03:00.25,0:03:03.40,EN,,0,0,0,,And this will now be not the explicit control evaluator for Lisp
Dialogue: 0,0:03:03.40,0:03:06.17,EN,,0,0,0,,this will be some register language for computing factorials.
Dialogue: 0,0:03:06.49,0:03:08.36,EN,,0,0,0,,So this is the translation of that.
Dialogue: 0,0:03:10.54,0:03:12.41,EN,,0,0,0,,That will go into some sort of loader
Dialogue: 0,0:03:13.35,0:03:15.21,EN,,0,0,0,,which will combine this code
Dialogue: 0,0:03:15.31,0:03:16.84,EN,,0,0,0,,with code selected from the library
Dialogue: 0,0:03:16.86,0:03:18.65,EN,,0,0,0,,to do things like primitive multiplication.
Dialogue: 0,0:03:19.82,0:03:21.69,EN,,0,0,0,,And then we'll produce a load module
Dialogue: 0,0:03:22.22,0:03:25.06,EN,,0,0,0,,which configures the register language machine
Dialogue: 0,0:03:25.06,0:03:27.24,EN,,0,0,0,,to be a special purpose factorial machine.
Dialogue: 0,0:03:28.12,0:03:30.22,EN,,0,0,0,,So that's a, that's a different strategy.
Dialogue: 0,0:03:30.22,0:03:31.22,EN,,0,0,0,,In interpretation,
Dialogue: 0,0:03:31.22,0:03:32.01,EN,,0,0,0,,we're raising
Dialogue: 0,0:03:32.91,0:03:35.23,EN,,0,0,0,,the machine to the level of our language, like Lisp.
Dialogue: 0,0:03:35.32,0:03:36.34,EN,,0,0,0,,In compilation
Dialogue: 0,0:03:36.34,0:03:38.43,EN,,0,0,0,,we're taking our program and lowering
Dialogue: 0,0:03:38.48,0:03:40.56,EN,,0,0,0,,it to the language that's spoken by the machine.
Dialogue: 0,0:03:41.96,0:03:43.84,EN,,0,0,0,,Well, how do these two strategies compare?
Dialogue: 0,0:03:44.30,0:03:49.42,EN,,0,0,0,,The compiler can produce code that will execute more efficiently.
Dialogue: 0,0:03:52.05,0:03:53.90,EN,,0,0,0,,The essential reason for that
Dialogue: 0,0:03:54.17,0:03:58.89,EN,,0,0,0,,is that if you think about the register operations that are running
Dialogue: 0,0:04:01.92,0:04:04.49,EN,,0,0,0,,the interpreter has to produce register operations
Dialogue: 0,0:04:04.97,0:04:06.75,EN,,0,0,0,,which, in principle, are going to be general enough
Dialogue: 0,0:04:07.32,0:04:08.94,EN,,0,0,0,,to execute any Lisp procedure.
Dialogue: 0,0:04:10.22,0:04:12.25,EN,,0,0,0,,Whereas the compiler only has to worry about
Dialogue: 0,0:04:12.27,0:04:14.92,EN,,0,0,0,,producing a special bunch of register operations for
Dialogue: 0,0:04:15.52,0:04:18.22,EN,,0,0,0,,for doing the particular Lisp procedure that you've compiled.
Dialogue: 0,0:04:20.17,0:04:21.20,EN,,0,0,0,,Or another way to say that
Dialogue: 0,0:04:21.20,0:04:25.31,EN,,0,0,0,,is that the interpreter is a general purpose simulator
Dialogue: 0,0:04:25.92,0:04:27.58,EN,,0,0,0,,that when you read in a Lisp procedure
Dialogue: 0,0:04:27.58,0:04:31.32,EN,,0,0,0,,then those can simulate the program described by that, by that procedure.
Dialogue: 0,0:04:31.32,0:04:33.87,EN,,0,0,0,,So the interpreter is worrying about making a general purpose simulator
Dialogue: 0,0:04:34.62,0:04:35.96,EN,,0,0,0,,whereas the compiler, in effect,
Dialogue: 0,0:04:36.00,0:04:37.68,EN,,0,0,0,,is configuring the thing to be the machine
Dialogue: 0,0:04:37.71,0:04:39.34,EN,,0,0,0,,that the interpreter would have been simulating.
Dialogue: 0,0:04:40.02,0:04:41.34,EN,,0,0,0,,So the compiler can be faster.
Dialogue: 0,0:04:52.55,0:04:53.64,EN,,0,0,0,,OK, On the other hand
Dialogue: 0,0:04:55.97,0:04:58.28,EN,,0,0,0,,the interpreter is a nicer environment for debugging.
Dialogue: 0,0:04:59.43,0:05:01.25,EN,,0,0,0,,And the reason for that is that we've got the
Dialogue: 0,0:05:01.57,0:05:03.02,EN,,0,0,0,,the source code actually there.
Dialogue: 0,0:05:03.02,0:05:04.81,EN,,0,0,0,,We're interpreting it That's what we're working with.
Dialogue: 0,0:05:05.87,0:05:07.69,EN,,0,0,0,,And we also have the library around.
Dialogue: 0,0:05:07.90,0:05:10.89,EN,,0,0,0,,See, the interpreter--the library sitting there is part of the interpreter.
Dialogue: 0,0:05:11.30,0:05:13.16,EN,,0,0,0,,The compiler only pulls out from the library
Dialogue: 0,0:05:13.20,0:05:14.56,EN,,0,0,0,,what it needs to run the program.
Dialogue: 0,0:05:14.87,0:05:17.00,EN,,0,0,0,,So if you're in the middle of debugging
Dialogue: 0,0:05:18.00,0:05:20.72,EN,,0,0,0,,and you might like to write a little extra program
Dialogue: 0,0:05:20.80,0:05:22.57,EN,,0,0,0,,to examine some run time data structure
Dialogue: 0,0:05:23.05,0:05:24.25,EN,,0,0,0,,or to produce some computation
Dialogue: 0,0:05:24.30,0:05:25.92,EN,,0,0,0,,that you didn't think of when you wrote the program
Dialogue: 0,0:05:25.95,0:05:27.53,EN,,0,0,0,,the interpreter can do that perfectly well
Dialogue: 0,0:05:28.05,0:05:29.21,EN,,0,0,0,,whereas the compiler can't.
Dialogue: 0,0:05:29.62,0:05:31.90,EN,,0,0,0,,So there are sort of dual, dual advantages.
Dialogue: 0,0:05:31.90,0:05:34.48,EN,,0,0,0,,The compiler will produce code that executes faster.
Dialogue: 0,0:05:34.85,0:05:37.02,EN,,0,0,0,,The interpreter is a better environment for debugging.
Dialogue: 0,0:05:38.95,0:05:41.40,EN,,0,0,0,,And most Lisp systems end up having both
Dialogue: 0,0:05:42.92,0:05:45.23,EN,,0,0,0,,end up being configured so you have an interpreter
Dialogue: 0,0:05:45.24,0:05:47.08,EN,,0,0,0,,that you use when you're developing your code.
Dialogue: 0,0:05:47.08,0:05:48.62,EN,,0,0,0,,Then you can speed it up by compiling.
Dialogue: 0,0:05:49.02,0:05:50.03,EN,,0,0,0,,And very often,
Dialogue: 0,0:05:50.04,0:05:51.68,EN,,0,0,0,,you can arrange that compiled code
Dialogue: 0,0:05:51.69,0:05:53.56,EN,,0,0,0,,and interpreted code can call each other.
Dialogue: 0,0:05:54.60,0:05:56.33,EN,,0,0,0,,We'll see how to do that, That's not hard.
Dialogue: 0,0:05:59.27,0:05:59.85,EN,,0,0,0,,OK
Dialogue: 0,0:06:00.97,0:06:02.09,EN,,0,0,0,,In fact, the way we'll--
Dialogue: 0,0:06:04.30,0:06:05.75,EN,,0,0,0,,in the compiler we're going to make
Dialogue: 0,0:06:05.75,0:06:07.58,EN,,0,0,0,,the way we'll arrange for compiled coding
Dialogue: 0,0:06:07.58,0:06:09.45,EN,,0,0,0,,and interpreted code to call to call each other
Dialogue: 0,0:06:09.90,0:06:12.06,EN,,0,0,0,,is that we'll have the compiler use exactly
Dialogue: 0,0:06:12.11,0:06:14.40,EN,,0,0,0,,the same register conventions as the interpreter.
Dialogue: 0,0:06:18.42,0:06:21.72,EN,,0,0,0,,Well, the idea of a compiler
Dialogue: 0,0:06:21.76,0:06:25.74,EN,,0,0,0,,is very much like the idea of an interpreter or evaluator.
Dialogue: 0,0:06:25.87,0:06:26.46,EN,,0,0,0,,It's the same thing.
Dialogue: 0,0:06:27.05,0:06:29.39,EN,,0,0,0,,See, the evaluator walks over the code
Dialogue: 0,0:06:29.82,0:06:32.35,EN,,0,0,0,,and performs some register operations.
Dialogue: 0,0:06:33.65,0:06:34.97,EN,,0,0,0,,That's what we did yesterday.
Dialogue: 0,0:06:37.10,0:06:40.27,EN,,0,0,0,,Well, the compiler essentially would like to walk over the code
Dialogue: 0,0:06:40.52,0:06:43.00,EN,,0,0,0,,and produce the register operations
Dialogue: 0,0:06:43.04,0:06:44.67,EN,,0,0,0,,that the evaluator would have done
Dialogue: 0,0:06:45.23,0:06:46.64,EN,,0,0,0,,were it evaluating the thing.
Dialogue: 0,0:06:48.60,0:06:49.95,EN,,0,0,0,,And that gives us some model
Dialogue: 0,0:06:50.60,0:06:53.77,EN,,0,0,0,,for how to implement a zeroth-order compiler
Dialogue: 0,0:06:55.30,0:06:58.32,EN,,0,0,0,,a very bad compiler but essentially a compiler.
Dialogue: 0,0:06:58.32,0:06:59.32,EN,,0,0,0,,A model for doing that
Dialogue: 0,0:06:59.36,0:07:00.59,EN,,0,0,0,,is you just take the evaluator,
Dialogue: 0,0:07:00.68,0:07:01.88,EN,,0,0,0,,you run it over the code
Dialogue: 0,0:07:02.80,0:07:06.06,EN,,0,0,0,,but instead of executing the actual operations
Dialogue: 0,0:07:06.06,0:07:07.15,EN,,0,0,0,,you just save them away.
Dialogue: 0,0:07:07.55,0:07:08.82,EN,,0,0,0,,And that's your compiled code.
Dialogue: 0,0:07:08.82,0:07:10.24,EN,,0,0,0,,So let me give you an example of that.
Dialogue: 0,0:07:12.70,0:07:14.14,EN,,0,0,0,,Suppose we're going to compile--
Dialogue: 0,0:07:15.10,0:07:17.90,EN,,0,0,0,,Suppose we want to compile the expression f of x.
Dialogue: 0,0:07:25.07,0:07:25.96,EN,,0,0,0,,So let's assume that
Dialogue: 0,0:07:25.96,0:07:28.06,EN,,0,0,0,,we've got f of x in the exp register
Dialogue: 0,0:07:28.06,0:07:29.55,EN,,0,0,0,,and something in the environment register.
Dialogue: 0,0:07:30.10,0:07:32.20,EN,,0,0,0,,And now imagine starting up the evaluator.
Dialogue: 0,0:07:34.60,0:07:35.71,EN,,0,0,0,,Well, it looks at the expression
Dialogue: 0,0:07:35.71,0:07:37.36,EN,,0,0,0,,and it sees that it's an application.
Dialogue: 0,0:07:37.92,0:07:41.90,EN,,0,0,0,,And it branches to a place in the
Dialogue: 0,0:07:42.52,0:07:45.15,EN,,0,0,0,,in the evaluator code we saw called ev-application.
Dialogue: 0,0:07:47.12,0:07:48.12,EN,,0,0,0,,And then it begins.
Dialogue: 0,0:07:48.16,0:07:50.08,EN,,0,0,0,,It stores away the operands and unev
Dialogue: 0,0:07:50.08,0:07:52.44,EN,,0,0,0,,and then it's going to put the operator in exp,
Dialogue: 0,0:07:52.48,0:07:54.27,EN,,0,0,0,,and it's going to go recursively evaluate it.
Dialogue: 0,0:07:54.47,0:07:56.08,EN,,0,0,0,,That's the process that we walk through.
Dialogue: 0,0:07:56.67,0:07:57.84,EN,,0,0,0,,And if you start looking at the code,
Dialogue: 0,0:07:57.87,0:07:59.74,EN,,0,0,0,,you start seeing some register operations.
Dialogue: 0,0:08:00.20,0:08:02.30,EN,,0,0,0,,You see assign to unev the operands
Dialogue: 0,0:08:02.30,0:08:03.95,EN,,0,0,0,,assign to exp the operator,
Dialogue: 0,0:08:04.09,0:08:06.20,EN,,0,0,0,,save the environment, generate that, and so on.
Dialogue: 0,0:08:10.22,0:08:11.93,EN,,0,0,0,,Well, if we look on the overhead here
Dialogue: 0,0:08:15.75,0:08:19.58,EN,,0,0,0,,we can see those operations starting to be produced.
Dialogue: 0,0:08:20.82,0:08:22.52,EN,,0,0,0,,Here's sort of the first real operation
Dialogue: 0,0:08:22.72,0:08:24.80,EN,,0,0,0,,that the evaluator would have done.
Dialogue: 0,0:08:25.00,0:08:27.20,EN,,0,0,0,,It pulls the operands out of the exp register
Dialogue: 0,0:08:27.47,0:08:28.62,EN,,0,0,0,,and assigns it to unev.
Dialogue: 0,0:08:30.03,0:08:32.27,EN,,0,0,0,,And then it assigns something to the expression register,
Dialogue: 0,0:08:32.30,0:08:33.46,EN,,0,0,0,,and it saves continue
Dialogue: 0,0:08:33.46,0:08:34.62,EN,,0,0,0,,and it saves env.
Dialogue: 0,0:08:34.62,0:08:38.65,EN,,0,0,0,,And all I'm doing here is writing down the register assignments
Dialogue: 0,0:08:39.57,0:08:42.32,EN,,0,0,0,,that the evaluator would have done in executing that code.
Dialogue: 0,0:08:42.77,0:08:43.79,EN,,0,0,0,,And can zoom out a little bit.
Dialogue: 0,0:08:44.30,0:08:47.13,EN,,0,0,0,,Altogether, there are about 19 operations there.
Dialogue: 0,0:08:49.40,0:08:51.64,EN,,0,0,0,,And this is the--this will be the piece of code
Dialogue: 0,0:08:52.05,0:08:53.90,EN,,0,0,0,,up until the point where
Dialogue: 0,0:08:54.75,0:08:57.10,EN,,0,0,0,,the evaluator branches off to apply-dispatch.
Dialogue: 0,0:08:57.86,0:08:59.16,EN,,0,0,0,,And in fact, in this compiler
Dialogue: 0,0:08:59.20,0:09:01.18,EN,,0,0,0,,we're not going to worry about apply-dispatch at all.
Dialogue: 0,0:09:01.30,0:09:02.11,EN,,0,0,0,,We're going to have everything
Dialogue: 0,0:09:02.35,0:09:05.04,EN,,0,0,0,,we're going to have both interpreted code and compiled code.
Dialogue: 0,0:09:06.07,0:09:07.61,EN,,0,0,0,,Always evaluate procedures,
Dialogue: 0,0:09:07.61,0:09:09.85,EN,,0,0,0,,always apply procedures by going to apply-dispatch.
Dialogue: 0,0:09:10.27,0:09:12.32,EN,,0,0,0,,That will easily allow interpreted code and
Dialogue: 0,0:09:12.36,0:09:13.71,EN,,0,0,0,,compiled code to call each other.
Dialogue: 0,0:09:18.27,0:09:19.87,EN,,0,0,0,,Well, in principle, that's all we need to do.
Dialogue: 0,0:09:21.05,0:09:22.66,EN,,0,0,0,,You just run the evaluator.
Dialogue: 0,0:09:22.66,0:09:24.50,EN,,0,0,0,,So the compiler's a lot like the evaluator.
Dialogue: 0,0:09:24.50,0:09:26.47,EN,,0,0,0,,You run it, except it stashes away these operations
Dialogue: 0,0:09:26.47,0:09:28.40,EN,,0,0,0,,instead of actually executing them.
Dialogue: 0,0:09:29.35,0:09:31.39,EN,,0,0,0,,Well, that's not, that's not quite true. there's
Dialogue: 0,0:09:32.91,0:09:34.99,EN,,0,0,0,,There's only one little lie in that.
Dialogue: 0,0:09:36.24,0:09:39.29,EN,,0,0,0,,What you have to worry about is if you have a, a predicate.
Dialogue: 0,0:09:40.12,0:09:42.16,EN,,0,0,0,,If you have some kind of test you want to do
Dialogue: 0,0:09:43.45,0:09:46.03,EN,,0,0,0,,obviously, at the point when you're compiling it
Dialogue: 0,0:09:46.52,0:09:47.98,EN,,0,0,0,,you don't know which branch of these--
Dialogue: 0,0:09:48.32,0:09:50.14,EN,,0,0,0,,of a conditional like this you're going to do.
Dialogue: 0,0:09:51.13,0:09:53.92,EN,,0,0,0,,So you can't say which one the evaluator would have done.
Dialogue: 0,0:09:54.90,0:09:57.12,EN,,0,0,0,,So all you do there is very simple.
Dialogue: 0,0:09:57.12,0:09:58.49,EN,,0,0,0,,You compile both branches.
Dialogue: 0,0:09:59.32,0:10:01.29,EN,,0,0,0,,So you compile a structure that looks like this.
Dialogue: 0,0:10:02.00,0:10:03.98,EN,,0,0,0,,That'll compile into something that says,
Dialogue: 0,0:10:05.31,0:10:09.15,EN,,0,0,0,,the code, the code for P.
Dialogue: 0,0:10:10.71,0:10:16.51,EN,,0,0,0,,And it puts its results in, say, the val register.
Dialogue: 0,0:10:18.17,0:10:20.64,EN,,0,0,0,,So you walk the interpreter over the predicate
Dialogue: 0,0:10:21.35,0:10:24.19,EN,,0,0,0,,and make sure that the result would go into the val register.
Dialogue: 0,0:10:24.70,0:10:27.22,EN,,0,0,0,,And then you compile an instruction that says
Dialogue: 0,0:10:27.22,0:10:33.79,EN,,0,0,0,,branch if, if val is true
Dialogue: 0,0:10:37.17,0:10:38.75,EN,,0,0,0,,to a place we'll call label one.
Dialogue: 0,0:10:44.97,0:10:47.52,EN,,0,0,0,,Then we, we will put the code for B
Dialogue: 0,0:10:49.42,0:10:52.32,EN,,0,0,0,,to walk the interpreter--walk the interpreter over B.
Dialogue: 0,0:10:53.62,0:10:57.21,EN,,0,0,0,,And then go to put in an instruction that says,
Dialogue: 0,0:10:57.23,0:10:58.75,EN,,0,0,0,,go to the next thing, whatever
Dialogue: 0,0:11:02.20,0:11:04.56,EN,,0,0,0,,whatever was supposed to happen after this thing was done.
Dialogue: 0,0:11:04.95,0:11:06.09,EN,,0,0,0,,You put in that instruction.
Dialogue: 0,0:11:06.88,0:11:08.62,EN,,0,0,0,,And here you put label one.
Dialogue: 0,0:11:12.12,0:11:13.80,EN,,0,0,0,,And here you put the code for A.
Dialogue: 0,0:11:19.47,0:11:25.85,EN,,0,0,0,,And you put go to next thing.
Dialogue: 0,0:11:31.42,0:11:32.88,EN,,0,0,0,,So that's how you treat a conditional.
Dialogue: 0,0:11:32.98,0:11:34.65,EN,,0,0,0,,You generate a little block like that.
Dialogue: 0,0:11:35.75,0:11:38.12,EN,,0,0,0,,And other than that
Dialogue: 0,0:11:38.95,0:11:41.55,EN,,0,0,0,,this zeroth-order compiler is the same as the evaluator.
Dialogue: 0,0:11:42.55,0:11:45.12,EN,,0,0,0,,It's just stashing away the instructions instead of executing them.
Dialogue: 0,0:11:46.55,0:11:47.60,EN,,0,0,0,,That seems pretty simple,
Dialogue: 0,0:11:47.64,0:11:49.08,EN,,0,0,0,,but we've gained something by that.
Dialogue: 0,0:11:50.12,0:11:52.62,EN,,0,0,0,,See, already that's going to be more efficient than the evaluator.
Dialogue: 0,0:11:53.52,0:11:56.14,EN,,0,0,0,,Because, if you watch the evaluator run
Dialogue: 0,0:11:56.35,0:12:01.05,EN,,0,0,0,,it's not only generating the register operations we wrote down
Dialogue: 0,0:12:01.27,0:12:03.50,EN,,0,0,0,,it's also doing things to decide which ones to generate.
Dialogue: 0,0:12:04.70,0:12:07.23,EN,,0,0,0,,So the very first thing it does, say here
Dialogue: 0,0:12:07.92,0:12:09.77,EN,,0,0,0,,here for instance, is go do some tests
Dialogue: 0,0:12:09.77,0:12:11.56,EN,,0,0,0,,and decide that this is an application
Dialogue: 0,0:12:13.57,0:12:15.05,EN,,0,0,0,,and then branch off to the place that,
Dialogue: 0,0:12:15.39,0:12:16.62,EN,,0,0,0,,that handles applications.
Dialogue: 0,0:12:16.62,0:12:18.44,EN,,0,0,0,,In other words, what the evaluator's doing
Dialogue: 0,0:12:18.62,0:12:22.76,EN,,0,0,0,,is simultaneously analyzing the code to see what to do
Dialogue: 0,0:12:23.47,0:12:24.99,EN,,0,0,0,,and running these operations.
Dialogue: 0,0:12:25.55,0:12:28.28,EN,,0,0,0,,And when you-- if you run the evaluator a million times
Dialogue: 0,0:12:28.28,0:12:30.30,EN,,0,0,0,,that analysis phase happens a million times
Dialogue: 0,0:12:30.85,0:12:32.58,EN,,0,0,0,,whereas in the compiler, it's happened once
Dialogue: 0,0:12:32.58,0:12:34.81,EN,,0,0,0,,and then you just have the register operations themselves.
Dialogue: 0,0:12:39.20,0:12:41.68,EN,,0,0,0,,Ok, that's a, a zeroth-order compiler
Dialogue: 0,0:12:41.80,0:12:44.04,EN,,0,0,0,,but it is a wretched, wretched compiler.
Dialogue: 0,0:12:44.45,0:12:45.28,EN,,0,0,0,,It's really dumb.
Dialogue: 0,0:12:46.90,0:12:48.41,EN,,0,0,0,,Let's--let's go back and,
Dialogue: 0,0:12:49.88,0:12:50.97,EN,,0,0,0,,and look at this overhead.
Dialogue: 0,0:12:52.02,0:12:55.29,EN,,0,0,0,,So look at look at some of the operations this thing is doing.
Dialogue: 0,0:12:55.85,0:12:56.88,EN,,0,0,0,,We're supposedly
Dialogue: 0,0:12:59.72,0:13:02.28,EN,,0,0,0,,looking at the operations in interpreting f of x.
Dialogue: 0,0:13:03.52,0:13:04.84,EN,,0,0,0,,Now, look here what it's doing.
Dialogue: 0,0:13:05.17,0:13:06.11,EN,,0,0,0,,For example, here
Dialogue: 0,0:13:07.15,0:13:11.98,EN,,0,0,0,,it assigns to exp the operator in fetch of exp.
Dialogue: 0,0:13:13.75,0:13:15.87,EN,,0,0,0,,But see, there's no reason to do that, because this is--
Dialogue: 0,0:13:16.22,0:13:17.47,EN,,0,0,0,,the compiler knows
Dialogue: 0,0:13:17.66,0:13:21.84,EN,,0,0,0,,that the operator, fetch of exp,  is f right here.
Dialogue: 0,0:13:23.35,0:13:25.56,EN,,0,0,0,,So there's no reason why this instruction should say that.
Dialogue: 0,0:13:25.70,0:13:28.88,EN,,0,0,0,,It should say, we'll assign to exp, f.
Dialogue: 0,0:13:29.45,0:13:31.08,EN,,0,0,0,,Or in fact, you don't need exp at all.
Dialogue: 0,0:13:31.87,0:13:33.56,EN,,0,0,0,,There's no reason it should have exp at all.
Dialogue: 0,0:13:33.56,0:13:35.16,EN,,0,0,0,,What, what did exp get used for?
Dialogue: 0,0:13:35.18,0:13:36.33,EN,,0,0,0,,Well, if we come down here
Dialogue: 0,0:13:40.77,0:13:42.20,EN,,0,0,0,,we're going to assign to val
Dialogue: 0,0:13:43.05,0:13:47.34,EN,,0,0,0,,look up the stuff in exp in the environment.
Dialogue: 0,0:13:48.68,0:13:49.53,EN,,0,0,0,,So what we really should do
Dialogue: 0,0:13:49.55,0:13:51.54,EN,,0,0,0,,get rid of the exp register altogether
Dialogue: 0,0:13:51.54,0:13:53.32,EN,,0,0,0,,and just change this instruction to say,
Dialogue: 0,0:13:53.34,0:13:54.16,EN,,0,0,0,,assign to val
Dialogue: 0,0:13:54.45,0:13:56.06,EN,,0,0,0,,look up the variable value
Dialogue: 0,0:13:56.36,0:13:58.40,EN,,0,0,0,,of the symbol f in the environment.
Dialogue: 0,0:14:01.09,0:14:01.77,EN,,0,0,0,,Similarly
Dialogue: 0,0:14:02.57,0:14:04.27,EN,,0,0,0,,back up here, we don't need unev at all
Dialogue: 0,0:14:04.72,0:14:05.79,EN,,0,0,0,,because we know
Dialogue: 0,0:14:06.22,0:14:09.16,EN,,0,0,0,,what the operands of fetch of exp are for this piece of code.
Dialogue: 0,0:14:09.16,0:14:10.62,EN,,0,0,0,,It's the, it's the list x.
Dialogue: 0,0:14:13.25,0:14:14.06,EN,,0,0,0,,So in some sense
Dialogue: 0,0:14:16.17,0:14:19.39,EN,,0,0,0,,you don't want unev and exp at all.
Dialogue: 0,0:14:19.67,0:14:21.05,EN,,0,0,0,,See, what they really are in some sense,
Dialogue: 0,0:14:21.08,0:14:25.30,EN,,0,0,0,,those aren't registers of the actual machine that's supposed to run.
Dialogue: 0,0:14:25.30,0:14:26.40,EN,,0,0,0,,Those are registers
Dialogue: 0,0:14:26.60,0:14:29.50,EN,,0,0,0,,that have to do with arranging the thing that can simulate that machine.
Dialogue: 0,0:14:30.72,0:14:33.77,EN,,0,0,0,,So they're always going to hold expressions
Dialogue: 0,0:14:34.00,0:14:36.04,EN,,0,0,0,,which from the compiler's point of view,
Dialogue: 0,0:14:36.06,0:14:36.81,EN,,0,0,0,,are just constants,
Dialogue: 0,0:14:36.95,0:14:38.48,EN,,0,0,0,,so can be put right into the code.
Dialogue: 0,0:14:39.47,0:14:41.34,EN,,0,0,0,,So you can forget about all the operations
Dialogue: 0,0:14:41.36,0:14:42.54,EN,,0,0,0,,worrying about exp and unev
Dialogue: 0,0:14:42.57,0:14:43.77,EN,,0,0,0,,and just use those constants.
Dialogue: 0,0:14:44.02,0:14:48.00,EN,,0,0,0,,Similarly, again, if we go, go back and look here
Dialogue: 0,0:14:48.00,0:14:51.32,EN,,0,0,0,,there are things like assign to continue eval-args.
Dialogue: 0,0:14:53.75,0:14:55.39,EN,,0,0,0,,Now, that has nothing to do with anything.
Dialogue: 0,0:14:55.62,0:14:57.76,EN,,0,0,0,,That was just the evaluator
Dialogue: 0,0:14:58.08,0:15:00.17,EN,,0,0,0,,keeping track of where it should go next
Dialogue: 0,0:15:02.70,0:15:05.96,EN,,0,0,0,,to evaluate the arguments in some, in some application.
Dialogue: 0,0:15:06.82,0:15:08.65,EN,,0,0,0,,But of course, that's irrelevant to the compiler,
Dialogue: 0,0:15:08.65,0:15:13.88,EN,,0,0,0,,because you-- the analysis phase will have already done that.
Dialogue: 0,0:15:15.05,0:15:16.83,EN,,0,0,0,,So this is completely irrelevant.
Dialogue: 0,0:15:17.70,0:15:19.32,EN,,0,0,0,,So a lot of these, these assignments
Dialogue: 0,0:15:19.32,0:15:21.30,EN,,0,0,0,,to continue have not to do
Dialogue: 0,0:15:21.30,0:15:24.62,EN,,0,0,0,,where the running machine is supposed to continue
Dialogue: 0,0:15:24.64,0:15:25.77,EN,,0,0,0,,in keeping track of its state.
Dialogue: 0,0:15:26.07,0:15:28.72,EN,,0,0,0,,It has to, to do with where the evaluator analysis should continue
Dialogue: 0,0:15:28.72,0:15:30.03,EN,,0,0,0,,and those are completely irrelevant.
Dialogue: 0,0:15:30.06,0:15:31.23,EN,,0,0,0,,So we can get rid of them.
Dialogue: 0,0:15:43.90,0:15:45.98,EN,,0,0,0,,Ok, well, if we, if we simply do that,
Dialogue: 0,0:15:46.16,0:15:47.75,EN,,0,0,0,,make those kinds of optimizations
Dialogue: 0,0:15:47.75,0:15:51.64,EN,,0,0,0,,get rid, get rid of worrying about exp and unev
Dialogue: 0,0:15:51.75,0:15:56.22,EN,,0,0,0,,and get rid of these irrelevant register assignments to continue
Dialogue: 0,0:15:57.25,0:15:59.96,EN,,0,0,0,,then we can take this literal code
Dialogue: 0,0:16:01.48,0:16:06.20,EN,,0,0,0,,these sort of 19 instructions that the evaluator would have done
Dialogue: 0,0:16:06.91,0:16:08.12,EN,,0,0,0,,and then replace them.
Dialogue: 0,0:16:08.36,0:16:10.33,EN,,0,0,0,,Let's look at the, at the slide.
Dialogue: 0,0:16:12.27,0:16:15.34,EN,,0,0,0,,Replace them by--we get rid of about half of them.
Dialogue: 0,0:16:18.28,0:16:20.75,EN,,0,0,0,,And again, this is just sort of filtering
Dialogue: 0,0:16:21.07,0:16:24.46,EN,,0,0,0,,what the evaluator would have done by getting rid of the irrelevant stuff.
Dialogue: 0,0:16:25.17,0:16:26.22,EN,,0,0,0,,And you see, for instance
Dialogue: 0,0:16:27.47,0:16:29.66,EN,,0,0,0,,here the--where the evaluator said,
Dialogue: 0,0:16:29.68,0:16:32.43,EN,,0,0,0,,assign val, look up variable value, fetch of exp
Dialogue: 0,0:16:32.46,0:16:34.22,EN,,0,0,0,,here we have put in the constant f.
Dialogue: 0,0:16:35.44,0:16:37.02,EN,,0,0,0,,Here we've put in the constant x.
Dialogue: 0,0:16:40.02,0:16:42.41,EN,,0,0,0,,So there's a, there's a little better compiler.
Dialogue: 0,0:16:43.79,0:16:46.76,EN,,0,0,0,,It's still pretty dumb.
Dialogue: 0,0:16:47.95,0:16:49.58,EN,,0,0,0,,It's still doing a lot of dumb things.
Dialogue: 0,0:16:50.45,0:16:52.52,EN,,0,0,0,,Again, if we go look at the slide again
Dialogue: 0,0:16:52.88,0:16:53.93,EN,,0,0,0,,look at the very beginning here
Dialogue: 0,0:16:56.34,0:16:58.17,EN,,0,0,0,,we see a save the environment
Dialogue: 0,0:16:59.35,0:17:01.72,EN,,0,0,0,,assign something to the val register
Dialogue: 0,0:17:01.80,0:17:03.35,EN,,0,0,0,,and restore the environment.
Dialogue: 0,0:17:03.35,0:17:04.41,EN,,0,0,0,,Where'd that come from?
Dialogue: 0,0:17:04.91,0:17:07.10,EN,,0,0,0,,That came from the evaluator back here saying
Dialogue: 0,0:17:07.15,0:17:10.28,EN,,0,0,0,,oh, I'm in the middle of evaluating an application.
Dialogue: 0,0:17:11.10,0:17:14.68,EN,,0,0,0,,So I'm going to recursively call eval dispatch.
Dialogue: 0,0:17:15.87,0:17:17.98,EN,,0,0,0,,So I'd better save the thing I'm going to need later,
Dialogue: 0,0:17:17.98,0:17:19.08,EN,,0,0,0,,which is the environment.
Dialogue: 0,0:17:19.77,0:17:22.86,EN,,0,0,0,,This was the result of recursively calling eval dispatch.
Dialogue: 0,0:17:23.47,0:17:25.77,EN,,0,0,0,,It was evaluating the symbol f in that case.
Dialogue: 0,0:17:26.50,0:17:28.27,EN,,0,0,0,,Then it came back from eval dispatch,
Dialogue: 0,0:17:28.28,0:17:29.66,EN,,0,0,0,,restored the environment.
Dialogue: 0,0:17:31.25,0:17:32.28,EN,,0,0,0,,But in fact,
Dialogue: 0,0:17:32.59,0:17:35.88,EN,,0,0,0,,the actual thing it ended up doing in the evaluation
Dialogue: 0,0:17:35.92,0:17:37.71,EN,,0,0,0,,is not going to hurt the environment at all.
Dialogue: 0,0:17:38.67,0:17:40.80,EN,,0,0,0,,So there's no reason to be saving the environment
Dialogue: 0,0:17:40.84,0:17:42.22,EN,,0,0,0,,and restoring the environment here.
Dialogue: 0,0:17:45.67,0:17:46.62,EN,,0,0,0,,Similarly
Dialogue: 0,0:17:49.79,0:17:51.39,EN,,0,0,0,,here I'm saving the argument list.
Dialogue: 0,0:17:53.07,0:17:55.80,EN,,0,0,0,,That's a piece of the argument evaluation loop,
Dialogue: 0,0:17:55.82,0:17:56.86,EN,,0,0,0,,saving the argument list
Dialogue: 0,0:17:57.20,0:17:58.03,EN,,0,0,0,,and here you restore it.
Dialogue: 0,0:17:58.08,0:18:00.51,EN,,0,0,0,,But the actual thing that you ended up doing
Dialogue: 0,0:18:00.80,0:18:02.28,EN,,0,0,0,,didn't trash the argument list.
Dialogue: 0,0:18:02.84,0:18:04.17,EN,,0,0,0,,So there was no reason to save it.
Dialogue: 0,0:18:08.65,0:18:12.88,EN,,0,0,0,,So another way to say, another way to say that
Dialogue: 0,0:18:13.77,0:18:14.80,EN,,0,0,0,,is that the,
Dialogue: 0,0:18:16.43,0:18:19.13,EN,,0,0,0,,the evaluator has to be maximally pessimistic
Dialogue: 0,0:18:19.87,0:18:21.07,EN,,0,0,0,,because as far from its point of view
Dialogue: 0,0:18:21.08,0:18:23.06,EN,,0,0,0,,it's just going off to evaluate something.
Dialogue: 0,0:18:23.24,0:18:24.97,EN,,0,0,0,,So it better save what it's going to need later.
Dialogue: 0,0:18:26.12,0:18:27.79,EN,,0,0,0,,But once you've done the analysis,
Dialogue: 0,0:18:27.82,0:18:29.68,EN,,0,0,0,,the compiler is in a position to say
Dialogue: 0,0:18:29.72,0:18:31.47,EN,,0,0,0,,well, what actually did I need to save?
Dialogue: 0,0:18:32.12,0:18:33.31,EN,,0,0,0,,And doesn't need to do any--
Dialogue: 0,0:18:33.42,0:18:37.30,EN,,0,0,0,,it doesn't need to be as careful as the evaluator
Dialogue: 0,0:18:37.30,0:18:38.80,EN,,0,0,0,,because it knows what it actually needs
Dialogue: 0,0:18:39.69,0:18:41.16,EN,,0,0,0,,Well, in any case, if we do that
Dialogue: 0,0:18:42.50,0:18:45.71,EN,,0,0,0,,and eliminate all those redundant saves and restores
Dialogue: 0,0:18:46.40,0:18:49.05,EN,,0,0,0,,then we can get it down to this.
Dialogue: 0,0:18:49.90,0:18:51.53,EN,,0,0,0,,And you see there are actually only three
Dialogue: 0,0:18:51.64,0:18:53.71,EN,,0,0,0,,only three instructions that we actually need
Dialogue: 0,0:18:54.07,0:18:55.72,EN,,0,0,0,,down from the initial 11 or so
Dialogue: 0,0:18:55.97,0:18:58.81,EN,,0,0,0,,or the initial 20 or so in the original one.
Dialogue: 0,0:18:59.87,0:19:00.92,EN,,0,0,0,,And that's just saying,
Dialogue: 0,0:19:01.12,0:19:03.18,EN,,0,0,0,,of those register operations
Dialogue: 0,0:19:03.27,0:19:04.94,EN,,0,0,0,,which ones did we actually need?
Dialogue: 0,0:19:09.42,0:19:11.74,EN,,0,0,0,,Let me just sort of summarize that in another way,
Dialogue: 0,0:19:11.74,0:19:13.48,EN,,0,0,0,,just to show you in a little better picture.
Dialogue: 0,0:19:16.00,0:19:17.52,EN,,0,0,0,,Here's a picture of starting--
Dialogue: 0,0:19:18.77,0:19:20.81,EN,,0,0,0,,This is looking at all the saves and restores.
Dialogue: 0,0:19:23.50,0:19:25.23,EN,,0,0,0,,So here's the expression, f of x
Dialogue: 0,0:19:25.32,0:19:27.87,EN,,0,0,0,,and then this traces through, on the bottom here
Dialogue: 0,0:19:28.75,0:19:31.80,EN,,0,0,0,,the various places in the evaluator
Dialogue: 0,0:19:34.97,0:19:38.04,EN,,0,0,0,,that were passed when the evaluation happened.
Dialogue: 0,0:19:38.04,0:19:40.01,EN,,0,0,0,,And then here, here you see arrows.
Dialogue: 0,0:19:40.22,0:19:42.08,EN,,0,0,0,,Arrow down means register saved.
Dialogue: 0,0:19:42.40,0:19:44.84,EN,,0,0,0,,So the first thing that happened is the environment got saved.
Dialogue: 0,0:19:46.82,0:19:48.68,EN,,0,0,0,,And over here, the environment got restored.
Dialogue: 0,0:19:52.38,0:19:54.54,EN,,0,0,0,,so there are all the pairs of stack operations.
Dialogue: 0,0:19:56.12,0:19:57.56,EN,,0,0,0,,Now, if you go ahead and say
Dialogue: 0,0:19:58.12,0:20:00.78,EN,,0,0,0,,well, let's remember that we don't--that unev
Dialogue: 0,0:20:00.89,0:20:03.02,EN,,0,0,0,,for instance, is a completely useless register.
Dialogue: 0,0:20:07.80,0:20:09.78,EN,,0,0,0,,And if we use the constant structure of the code
Dialogue: 0,0:20:09.78,0:20:12.52,EN,,0,0,0,,well, we don't need, we don't need to save unev.
Dialogue: 0,0:20:16.20,0:20:19.15,EN,,0,0,0,,And then, depending on how we set up the discipline of the--
Dialogue: 0,0:20:19.16,0:20:21.88,EN,,0,0,0,,of calling other things that apply,
Dialogue: 0,0:20:21.88,0:20:23.85,EN,,0,0,0,,we may or may not need to save continue.
Dialogue: 0,0:20:27.40,0:20:28.74,EN,,0,0,0,,That's the first step I did.
Dialogue: 0,0:20:28.74,0:20:30.51,EN,,0,0,0,,And then we can look and see what's actually,
Dialogue: 0,0:20:31.71,0:20:32.70,EN,,0,0,0,,what's actually needed.
Dialogue: 0,0:20:33.07,0:20:35.56,EN,,0,0,0,,See, we don't-- didn't really need to save env
Dialogue: 0,0:20:36.04,0:20:37.82,EN,,0,0,0,,across-evaluating f
Dialogue: 0,0:20:38.08,0:20:39.92,EN,,0,0,0,,because it wouldn't, it wouldn't trash it.
Dialogue: 0,0:20:39.92,0:20:41.31,EN,,0,0,0,,So if we take advantage of that
Dialogue: 0,0:20:44.12,0:20:47.56,EN,,0,0,0,,and see the evaluation of f here
Dialogue: 0,0:20:48.57,0:20:50.44,EN,,0,0,0,,doesn't really need to worry about,
Dialogue: 0,0:20:51.61,0:20:52.60,EN,,0,0,0,,about hurting env.
Dialogue: 0,0:20:52.60,0:20:54.94,EN,,0,0,0,,And similarly, the evaluation of x here
Dialogue: 0,0:20:57.17,0:20:58.89,EN,,0,0,0,,when the evaluator did that it said
Dialogue: 0,0:20:58.91,0:21:01.64,EN,,0,0,0,,Oh, I'd better preserve the function register around that
Dialogue: 0,0:21:02.07,0:21:03.22,EN,,0,0,0,,because I might need it later.
Dialogue: 0,0:21:03.28,0:21:04.89,EN,,0,0,0,,And I better preserve the argument list.
Dialogue: 0,0:21:06.90,0:21:09.05,EN,,0,0,0,,Whereas the compiler is now in a position to know
Dialogue: 0,0:21:09.05,0:21:10.38,EN,,0,0,0,,well, we didn't really need to save--
Dialogue: 0,0:21:10.52,0:21:11.84,EN,,0,0,0,,to do those saves and restores.
Dialogue: 0,0:21:12.70,0:21:16.09,EN,,0,0,0,,So in fact, all of the stack operations done by the evaluator
Dialogue: 0,0:21:16.32,0:21:19.58,EN,,0,0,0,,turned out to be unnecessary or overly pessimistic.
Dialogue: 0,0:21:19.62,0:21:21.45,EN,,0,0,0,,And the compiler is in a position to know that.
Dialogue: 0,0:21:27.35,0:21:28.48,EN,,0,0,0,,Well that's the basic idea.
Dialogue: 0,0:21:29.80,0:21:31.00,EN,,0,0,0,,We take the evaluator
Dialogue: 0,0:21:31.00,0:21:33.24,EN,,0,0,0,,we eliminate the things that you don't need
Dialogue: 0,0:21:33.24,0:21:35.24,EN,,0,0,0,,that in some sense have nothing to do with the compiler at all
Dialogue: 0,0:21:35.24,0:21:36.19,EN,,0,0,0,,just the evaluator
Dialogue: 0,0:21:37.40,0:21:40.40,EN,,0,0,0,,and then you see which stack operations are unnecessary.
Dialogue: 0,0:21:40.82,0:21:43.76,EN,,0,0,0,,That's the basic structure of the compiler that's
Dialogue: 0,0:21:43.85,0:21:45.04,EN,,0,0,0,,that's described in the book.
Dialogue: 0,0:21:45.04,0:21:47.00,EN,,0,0,0,,Let me just show you how a
Dialogue: 0,0:21:47.76,0:21:49.68,EN,,0,0,0,,that examples a little bit too simple.
Dialogue: 0,0:21:51.20,0:21:53.26,EN,,0,0,0,,To see how you, how you actually save a lot
Dialogue: 0,0:21:53.29,0:21:56.06,EN,,0,0,0,,let's look at a little bit more complicated expression.
Dialogue: 0,0:21:58.15,0:22:01.93,EN,,0,0,0,,(F (G X) 1)
Dialogue: 0,0:22:03.87,0:22:05.52,EN,,0,0,0,,And I'm not going to go through all the code.
Dialogue: 0,0:22:06.40,0:22:08.56,EN,,0,0,0,,There's a, there's a fair pile of it.
Dialogue: 0,0:22:09.72,0:22:12.35,EN,,0,0,0,,I think there are, there are something like 16
Dialogue: 0,0:22:12.35,0:22:14.67,EN,,0,0,0,,16 pairs of register saves and restores
Dialogue: 0,0:22:14.70,0:22:16.25,EN,,0,0,0,,as the evaluator walks through that.
Dialogue: 0,0:22:17.00,0:22:18.57,EN,,0,0,0,,Here's a diagram of them.
Dialogue: 0,0:22:20.57,0:22:21.95,EN,,0,0,0,,Let's see. You see what's going on.
Dialogue: 0,0:22:22.97,0:22:23.90,EN,,0,0,0,,You start out by--
Dialogue: 0,0:22:24.25,0:22:26.62,EN,,0,0,0,,the evaluator says, oh, I'm about to do an application.
Dialogue: 0,0:22:26.90,0:22:29.13,EN,,0,0,0,,I'll preserve the environment. I'll restore it here.
Dialogue: 0,0:22:30.65,0:22:34.44,EN,,0,0,0,,Then I'm about to do the first operand.
Dialogue: 0,0:22:36.81,0:22:39.28,EN,,0,0,0,,Here it recursively goes to the evaluator.
Dialogue: 0,0:22:39.28,0:22:40.89,EN,,0,0,0,,The evaluator says, oh, this is an application,
Dialogue: 0,0:22:40.91,0:22:42.10,EN,,0,0,0,,I'll save the environment
Dialogue: 0,0:22:42.10,0:22:44.97,EN,,0,0,0,,do the operator of that combination, restore it here.
Dialogue: 0,0:22:45.80,0:22:48.92,EN,,0,0,0,,This save--this restore matches that save.
Dialogue: 0,0:22:49.77,0:22:50.78,EN,,0,0,0,,And so on.
Dialogue: 0,0:22:51.65,0:22:52.51,EN,,0,0,0,,There's unev here,
Dialogue: 0,0:22:52.52,0:22:54.62,EN,,0,0,0,,which turns out to be completely unnecessary
Dialogue: 0,0:22:54.97,0:22:56.60,EN,,0,0,0,,continues getting bumped around here.
Dialogue: 0,0:22:57.42,0:23:00.41,EN,,0,0,0,,The function register is getting, getting saved
Dialogue: 0,0:23:00.78,0:23:04.36,EN,,0,0,0,,across the first operands, across the operands.
Dialogue: 0,0:23:05.10,0:23:06.52,EN,,0,0,0,,All sorts of things are going on.
Dialogue: 0,0:23:06.78,0:23:09.39,EN,,0,0,0,,But if you say, well, what of those really were the business of
Dialogue: 0,0:23:09.87,0:23:11.66,EN,,0,0,0,,the compiler as opposed to the evaluator
Dialogue: 0,0:23:12.27,0:23:13.55,EN,,0,0,0,,you get rid of a whole bunch.
Dialogue: 0,0:23:14.30,0:23:16.64,EN,,0,0,0,,And then on top of that, if you say things like
Dialogue: 0,0:23:19.40,0:23:22.54,EN,,0,0,0,,the evaluation of F doesn't hurt the environment register,
Dialogue: 0,0:23:23.82,0:23:26.51,EN,,0,0,0,,or simply looking up the symbol X,
Dialogue: 0,0:23:29.28,0:23:32.09,EN,,0,0,0,,you don't have to protect the function register against that.
Dialogue: 0,0:23:34.30,0:23:37.60,EN,,0,0,0,,So you come down to just a couple of, a couple of pairs here.
Dialogue: 0,0:23:40.25,0:23:42.27,EN,,0,0,0,,And still, you can do a little better.
Dialogue: 0,0:23:42.27,0:23:44.33,EN,,0,0,0,,Look what's going on here with the environment register.
Dialogue: 0,0:23:45.21,0:23:47.39,EN,,0,0,0,,The environment register comes along and says, oh,
Dialogue: 0,0:23:51.00,0:23:52.25,EN,,0,0,0,,here's a combination.
Dialogue: 0,0:23:54.33,0:23:55.69,EN,,0,0,0,,This evaluator, by the way,
Dialogue: 0,0:23:55.78,0:23:57.27,EN,,0,0,0,,doesn't know anything about G.
Dialogue: 0,0:23:58.57,0:24:00.73,EN,,0,0,0,,So here it says, so it says,
Dialogue: 0,0:24:01.29,0:24:03.45,EN,,0,0,0,,I'd better save the environment register,
Dialogue: 0,0:24:03.96,0:24:05.42,EN,,0,0,0,,because evaluating G might be
Dialogue: 0,0:24:05.42,0:24:07.42,EN,,0,0,0,,some arbitrary piece of code that would trash it
Dialogue: 0,0:24:07.55,0:24:09.45,EN,,0,0,0,,and I'm going to need it later,
Dialogue: 0,0:24:10.17,0:24:11.40,EN,,0,0,0,,after this argument,
Dialogue: 0,0:24:12.22,0:24:13.37,EN,,0,0,0,,for doing the second argument.
Dialogue: 0,0:24:15.60,0:24:17.24,EN,,0,0,0,,So that's why this one didn't go away,
Dialogue: 0,0:24:19.07,0:24:22.54,EN,,0,0,0,,because the compiler made no assumptions about what G would do.
Dialogue: 0,0:24:22.54,0:24:23.60,EN,,0,0,0,,On the other hand,
Dialogue: 0,0:24:24.61,0:24:26.52,EN,,0,0,0,,if you look at what the second argument is,
Dialogue: 0,0:24:26.64,0:24:27.70,EN,,0,0,0,,that's just looking up one.
Dialogue: 0,0:24:27.70,0:24:29.60,EN,,0,0,0,,That doesn't need this environment register.
Dialogue: 0,0:24:30.77,0:24:32.04,EN,,0,0,0,,So there's no reason to save it.
Dialogue: 0,0:24:32.06,0:24:33.77,EN,,0,0,0,,So in fact, you can get rid of that one, too.
Dialogue: 0,0:24:34.85,0:24:37.81,EN,,0,0,0,,And from this whole pile of, of register operations,
Dialogue: 0,0:24:37.98,0:24:40.08,EN,,0,0,0,,if you simply do a little bit of reasoning like that,
Dialogue: 0,0:24:40.55,0:24:43.05,EN,,0,0,0,,you get down to, I think, just two pairs of saves and restores.
Dialogue: 0,0:24:45.10,0:24:46.97,EN,,0,0,0,,And those, in fact, could go away further if you,
Dialogue: 0,0:24:47.52,0:24:49.08,EN,,0,0,0,,if you knew something about G.
Dialogue: 0,0:24:56.27,0:24:57.85,EN,,0,0,0,,So again, the general idea
Dialogue: 0,0:24:57.95,0:24:59.98,EN,,0,0,0,,is that the reason the compiler can be better
Dialogue: 0,0:24:59.98,0:25:02.56,EN,,0,0,0,,is that the interpreter doesn't know what it's about to encounter.
Dialogue: 0,0:25:03.25,0:25:05.04,EN,,0,0,0,,It has to be maximally pessimistic
Dialogue: 0,0:25:05.05,0:25:06.70,EN,,0,0,0,,to protect itself.
Dialogue: 0,0:25:07.90,0:25:08.76,EN,,0,0,0,,The compiler
Dialogue: 0,0:25:09.48,0:25:12.38,EN,,0,0,0,,only has to deal with what actually had to be saved.
Dialogue: 0,0:25:13.37,0:25:15.20,EN,,0,0,0,,And there are two reasons that something
Dialogue: 0,0:25:15.24,0:25:17.37,EN,,0,0,0,,might not have to be saved.
Dialogue: 0,0:25:17.82,0:25:18.70,EN,,0,0,0,,One is that
Dialogue: 0,0:25:18.70,0:25:19.82,EN,,0,0,0,,what you're protecting it against,
Dialogue: 0,0:25:19.95,0:25:21.44,EN,,0,0,0,,in fact, didn't trash the register,
Dialogue: 0,0:25:22.08,0:25:23.58,EN,,0,0,0,,like it was just a variable look-up.
Dialogue: 0,0:25:24.12,0:25:25.20,EN,,0,0,0,,And the other one is,
Dialogue: 0,0:25:25.32,0:25:27.10,EN,,0,0,0,,that the thing that you were saving it for
Dialogue: 0,0:25:28.28,0:25:29.92,EN,,0,0,0,,might turn out not to actually need it.
Dialogue: 0,0:25:30.81,0:25:34.27,EN,,0,0,0,,So those are the two basic pieces of knowledge
Dialogue: 0,0:25:34.30,0:25:35.88,EN,,0,0,0,,that the compiler can take advantage of
Dialogue: 0,0:25:36.27,0:25:37.76,EN,,0,0,0,,in making the code more efficient.
Dialogue: 0,0:25:44.27,0:25:45.32,EN,,0,0,0,,Let's break for questions.
Dialogue: 0,0:25:51.20,0:25:53.10,EN,,0,0,0,,AUDIENCE: You kept saying that the uneval register,
Dialogue: 0,0:25:53.13,0:25:56.40,EN,,0,0,0,,unev register didn't need to be used at all.
Dialogue: 0,0:25:56.41,0:25:58.68,EN,,0,0,0,,Does that mean that you could just map a six-register machine?
Dialogue: 0,0:25:58.70,0:26:00.08,EN,,0,0,0,,Or is that, in this particular example,
Dialogue: 0,0:26:00.11,0:26:01.18,EN,,0,0,0,,it didn't need to be used?
Dialogue: 0,0:26:01.72,0:26:02.81,EN,,0,0,0,,PROFESSOR: For the compiler,
Dialogue: 0,0:26:04.31,0:26:07.42,EN,,0,0,0,,you could generate code for the six-register, five, right?
Dialogue: 0,0:26:07.56,0:26:09.02,EN,,0,0,0,,Because that exp goes away also.
Dialogue: 0,0:26:09.40,0:26:14.57,EN,,0,0,0,,Assuming--yeah, you can get rid of both exp and unev
Dialogue: 0,0:26:14.57,0:26:16.87,EN,,0,0,0,,because, see, those are data structures of the evaluator.
Dialogue: 0,0:26:17.36,0:26:19.36,EN,,0,0,0,,Those are all things that would be constants
Dialogue: 0,0:26:19.39,0:26:20.87,EN,,0,0,0,,from the point of view of the compiler.
Dialogue: 0,0:26:21.65,0:26:22.44,EN,,0,0,0,,The only thing is
Dialogue: 0,0:26:22.48,0:26:24.59,EN,,0,0,0,,this particular compiler is set up
Dialogue: 0,0:26:24.79,0:26:27.92,EN,,0,0,0,,so that interpreted code and compiled code can coexist.
Dialogue: 0,0:26:29.32,0:26:30.72,EN,,0,0,0,,So the way to think about it is,
Dialogue: 0,0:26:30.97,0:26:32.29,EN,,0,0,0,,is maybe you build a chip
Dialogue: 0,0:26:34.30,0:26:35.50,EN,,0,0,0,,which is the evaluator,
Dialogue: 0,0:26:35.88,0:26:37.28,EN,,0,0,0,,and what the compiler might do
Dialogue: 0,0:26:37.31,0:26:39.02,EN,,0,0,0,,is generate code for that chip.
Dialogue: 0,0:26:40.40,0:26:41.90,EN,,0,0,0,,It just wouldn't use two of the registers.
Dialogue: 0,0:26:51.52,0:26:52.47,EN,,0,0,0,,All right, let's take a break.
Dialogue: 0,0:26:53.55,0:27:07.18,EN,,0,0,0,,[JESU, JOY OF MAN'S DESIRING]
Dialogue: 0,0:27:29.21,0:27:32.43,EN,,0,0,0,,We just looked at what the compiler is supposed to do.
Dialogue: 0,0:27:32.78,0:27:36.04,EN,,0,0,0,,Now let's very briefly look at how,
Dialogue: 0,0:27:36.15,0:27:37.47,EN,,0,0,0,,how this gets accomplished.
Dialogue: 0,0:27:38.26,0:27:39.58,EN,,0,0,0,,And I'm going to give no details.
Dialogue: 0,0:27:39.60,0:27:42.17,EN,,0,0,0,,There's, there's a giant pile of code in the book
Dialogue: 0,0:27:42.22,0:27:43.42,EN,,0,0,0,,that gives all the details.
Dialogue: 0,0:27:43.45,0:27:45.31,EN,,0,0,0,,But what I want to do is just show you the,
Dialogue: 0,0:27:45.96,0:27:47.26,EN,,0,0,0,,the essential idea here.
Dialogue: 0,0:27:49.49,0:27:51.36,EN,,0,0,0,,Worry about the details some other time.
Dialogue: 0,0:27:51.51,0:27:55.30,EN,,0,0,0,,Let's imagine that we're compiling an expression
Dialogue: 0,0:27:55.30,0:27:57.01,EN,,0,0,0,,that looks like there's some operator
Dialogue: 0,0:27:57.48,0:27:58.56,EN,,0,0,0,,and there are two arguments.
Dialogue: 0,0:28:03.56,0:28:04.24,EN,,0,0,0,,Now, the--
Dialogue: 0,0:28:06.27,0:28:08.14,EN,,0,0,0,,what's the code that the compiler should generate?
Dialogue: 0,0:28:08.85,0:28:09.78,EN,,0,0,0,,Well, first of all,
Dialogue: 0,0:28:09.83,0:28:11.20,EN,,0,0,0,,it should recursively go off
Dialogue: 0,0:28:11.90,0:28:13.28,EN,,0,0,0,,and compile the operator.
Dialogue: 0,0:28:14.37,0:28:19.02,EN,,0,0,0,,So it says, I'll compile the operator.
Dialogue: 0,0:28:21.16,0:28:24.54,EN,,0,0,0,,And where I'm going to need that
Dialogue: 0,0:28:24.84,0:28:27.95,EN,,0,0,0,,is to be in the function register, eventually.
Dialogue: 0,0:28:28.42,0:28:29.60,EN,,0,0,0,,So I'll compile some instructions
Dialogue: 0,0:28:29.64,0:28:31.56,EN,,0,0,0,,that will compile the operator
Dialogue: 0,0:28:31.69,0:28:38.62,EN,,0,0,0,,and end up with the result in the function register.
Dialogue: 0,0:28:45.51,0:28:46.94,EN,,0,0,0,,The next thing it's going to do,
Dialogue: 0,0:28:47.71,0:28:49.68,EN,,0,0,0,,another piece is to say,
Dialogue: 0,0:28:49.68,0:28:55.17,EN,,0,0,0,,I have to compile the first argument.
Dialogue: 0,0:28:55.17,0:28:56.80,EN,,0,0,0,,So it calls itself recursively.
Dialogue: 0,0:28:58.04,0:29:03.36,EN,,0,0,0,,And let's say the result will go into val.
Dialogue: 0,0:29:09.07,0:29:10.75,EN,,0,0,0,,And then what it's going to need to do is
Dialogue: 0,0:29:10.75,0:29:12.26,EN,,0,0,0,,start setting up the argument list.
Dialogue: 0,0:29:12.95,0:29:25.50,EN,,0,0,0,,So it'll say, assign to argl cons of fetch--
Dialogue: 0,0:29:25.55,0:29:27.10,EN,,0,0,0,,so it generates this literal instruction--
Dialogue: 0,0:29:27.50,0:29:32.51,EN,,0,0,0,,fetch of val onto empty list.
Dialogue: 0,0:29:35.00,0:29:36.05,EN,,0,0,0,,However,
Dialogue: 0,0:29:37.99,0:29:40.61,EN,,0,0,0,,it might have to work--  when it gets here,
Dialogue: 0,0:29:41.32,0:29:42.82,EN,,0,0,0,,it's going to need the environment.
Dialogue: 0,0:29:43.95,0:29:45.29,EN,,0,0,0,,It's going to need whatever environment was here
Dialogue: 0,0:29:45.32,0:29:48.21,EN,,0,0,0,,in order to do this evaluation of the first argument.
Dialogue: 0,0:29:49.04,0:29:51.18,EN,,0,0,0,,So it has to ensure that
Dialogue: 0,0:29:51.92,0:29:53.76,EN,,0,0,0,,the compilation of this operand,
Dialogue: 0,0:29:55.32,0:29:57.85,EN,,0,0,0,,or it has to protect the function register
Dialogue: 0,0:29:58.01,0:30:00.98,EN,,0,0,0,,against whatever might happen in the compilation of this operand.
Dialogue: 0,0:30:01.30,0:30:03.08,EN,,0,0,0,,So it puts a note here and says, oh,
Dialogue: 0,0:30:03.37,0:30:12.89,EN,,0,0,0,,this piece should be done preserving the environment register.
Dialogue: 0,0:30:17.39,0:30:18.44,EN,,0,0,0,,Similarly, here,
Dialogue: 0,0:30:21.02,0:30:23.30,EN,,0,0,0,,after it gets done compiling the first operand,
Dialogue: 0,0:30:23.57,0:30:24.67,EN,,0,0,0,,it's going to say, I'd better--
Dialogue: 0,0:30:24.71,0:30:27.92,EN,,0,0,0,,I'm going to need to know the environment for the second operand.
Dialogue: 0,0:30:27.92,0:30:29.46,EN,,0,0,0,,So it puts a little note here, saying,
Dialogue: 0,0:30:29.71,0:30:35.96,EN,,0,0,0,,yeah, this is also done preserving env.
Dialogue: 0,0:30:39.42,0:30:41.02,EN,,0,0,0,,Now it goes on and says, well,
Dialogue: 0,0:30:41.12,0:30:42.83,EN,,0,0,0,,the next chunk of code
Dialogue: 0,0:30:43.31,0:30:49.74,EN,,0,0,0,,is the one that's going to compile the second argument.
Dialogue: 0,0:30:50.82,0:30:52.64,EN,,0,0,0,,And let's say
Dialogue: 0,0:30:52.99,0:30:59.28,EN,,0,0,0,,And let's say it'll compile it with a targeted to val, as they say.
Dialogue: 0,0:31:03.86,0:31:06.70,EN,,0,0,0,,And then it'll generate the literal instruction,
Dialogue: 0,0:31:07.84,0:31:09.25,EN,,0,0,0,,building up the argument list.
Dialogue: 0,0:31:09.55,0:31:15.28,EN,,0,0,0,,So it'll say, assign to argl
Dialogue: 0,0:31:20.22,0:31:28.94,EN,,0,0,0,,cons of the new value it just got onto the old argument list.
Dialogue: 0,0:31:33.97,0:31:34.64,EN,,0,0,0,,However,
Dialogue: 0,0:31:34.81,0:31:36.58,EN,,0,0,0,,in order to have the old argument list,
Dialogue: 0,0:31:37.15,0:31:40.99,EN,,0,0,0,,it better have arranged that the argument list didn't get trashed
Dialogue: 0,0:31:41.30,0:31:42.69,EN,,0,0,0,,by whatever happened in here.
Dialogue: 0,0:31:43.50,0:31:45.17,EN,,0,0,0,,So it puts a little note here and says,
Dialogue: 0,0:31:45.34,0:31:51.64,EN,,0,0,0,,oh, this has to be done preserving argl.
Dialogue: 0,0:31:54.16,0:31:56.03,EN,,0,0,0,,Now it's got the argument list set up.
Dialogue: 0,0:31:58.01,0:32:02.86,EN,,0,0,0,,And it's all ready to go to apply dispatch.
Dialogue: 0,0:32:07.02,0:32:10.80,EN,,0,0,0,,It generates this literal instruction.
Dialogue: 0,0:32:15.19,0:32:17.37,EN,,0,0,0,,Because now it's got the arguments in argl
Dialogue: 0,0:32:18.15,0:32:20.59,EN,,0,0,0,,and the operator in fun,
Dialogue: 0,0:32:20.59,0:32:22.89,EN,,0,0,0,,but wait, it's only got the operator in fun
Dialogue: 0,0:32:23.27,0:32:26.64,EN,,0,0,0,,if it had ensured that this block of code
Dialogue: 0,0:32:27.09,0:32:29.27,EN,,0,0,0,,didn't trash what was in the function register.
Dialogue: 0,0:32:29.67,0:32:31.24,EN,,0,0,0,,So it puts a little note here and says,
Dialogue: 0,0:32:31.55,0:32:32.73,EN,,0,0,0,,oh, yes, all this stuff here
Dialogue: 0,0:32:34.88,0:32:40.73,EN,,0,0,0,,had better be done preserving the function register.
Dialogue: 0,0:32:43.71,0:32:46.15,EN,,0,0,0,,So that's the little--so when it starts ticking--
Dialogue: 0,0:32:46.15,0:32:47.10,EN,,0,0,0,,so basically, what the
Dialogue: 0,0:32:48.20,0:32:50.24,EN,,0,0,0,,what the compiler does is
Dialogue: 0,0:32:50.54,0:32:52.46,EN,,0,0,0,,append a whole bunch of code sequences.
Dialogue: 0,0:32:53.50,0:32:58.83,EN,,0,0,0,,See, what it's got in it is little primitive pieces of things
Dialogue: 0,0:32:58.86,0:33:00.12,EN,,0,0,0,,like how to look up a symbol,
Dialogue: 0,0:33:01.44,0:33:02.60,EN,,0,0,0,,how to do a conditional.
Dialogue: 0,0:33:02.64,0:33:05.44,EN,,0,0,0,,Those are all little pieces of things.
Dialogue: 0,0:33:05.44,0:33:07.99,EN,,0,0,0,,And then it appends them together in this sort of discipline.
Dialogue: 0,0:33:08.78,0:33:10.79,EN,,0,0,0,,So the basic means of combining things
Dialogue: 0,0:33:10.86,0:33:13.18,EN,,0,0,0,,is to append two code sequences.
Dialogue: 0,0:33:21.55,0:33:22.86,EN,,0,0,0,,That's what's going on here.
Dialogue: 0,0:33:25.58,0:33:27.24,EN,,0,0,0,,And it's a little bit tricky.
Dialogue: 0,0:33:27.56,0:33:30.37,EN,,0,0,0,,The idea is that it appends two code sequences,
Dialogue: 0,0:33:31.60,0:33:33.76,EN,,0,0,0,,taking care to preserve a register.
Dialogue: 0,0:33:35.63,0:33:37.93,EN,,0,0,0,,So the actual append operation looks like this.
Dialogue: 0,0:33:39.15,0:33:40.65,EN,,0,0,0,,What it wants to do is say, if--
Dialogue: 0,0:33:41.20,0:33:44.11,EN,,0,0,0,,here's what it means to append two code sequences.
Dialogue: 0,0:33:44.53,0:33:53.63,EN,,0,0,0,,So if sequence one needs register--
Dialogue: 0,0:33:53.66,0:33:54.72,EN,,0,0,0,,I should change this.
Dialogue: 0,0:33:54.72,0:33:56.87,EN,,0,0,0,,Append sequence one to sequence two,
Dialogue: 0,0:33:57.42,0:34:03.96,EN,,0,0,0,,preserving some register.
Dialogue: 0,0:34:08.52,0:34:09.91,EN,,0,0,0,,Let me say, and.
Dialogue: 0,0:34:11.36,0:34:13.03,EN,,0,0,0,,So it's clear that sequence one comes first.
Dialogue: 0,0:34:13.88,0:34:19.87,EN,,0,0,0,,So if sequence two needs the register
Dialogue: 0,0:34:21.12,0:34:27.85,EN,,0,0,0,,and sequence one modifies the register,
Dialogue: 0,0:34:33.68,0:34:36.30,EN,,0,0,0,,then the instructions that the compiler spits out,
Dialogue: 0,0:34:36.97,0:34:41.34,EN,,0,0,0,,are save the register.
Dialogue: 0,0:34:43.02,0:34:44.19,EN,,0,0,0,,Here's the code.
Dialogue: 0,0:34:44.35,0:34:45.35,EN,,0,0,0,,You generate this code.
Dialogue: 0,0:34:45.35,0:34:46.28,EN,,0,0,0,,Save the register,
Dialogue: 0,0:34:46.72,0:34:52.97,EN,,0,0,0,,and then you put out the recursively compiled stuff for sequence one.
Dialogue: 0,0:34:53.30,0:34:54.84,EN,,0,0,0,,And then you restore the register.
Dialogue: 0,0:35:00.52,0:35:03.92,EN,,0,0,0,,And then you put out the recursively compiled stuff
Dialogue: 0,0:35:04.46,0:35:05.47,EN,,0,0,0,,for sequence two.
Dialogue: 0,0:35:07.07,0:35:09.62,EN,,0,0,0,,That's in the case where you need to do it.
Dialogue: 0,0:35:09.62,0:35:11.82,EN,,0,0,0,,Sequence two actually needs the register,
Dialogue: 0,0:35:11.82,0:35:13.74,EN,,0,0,0,,and sequence one actually clobbers it.
Dialogue: 0,0:35:15.12,0:35:17.07,EN,,0,0,0,,So that's sort of if. Otherwise,
Dialogue: 0,0:35:20.50,0:35:26.57,EN,,0,0,0,,all you spit out is sequence one followed by sequence two.
Dialogue: 0,0:35:28.17,0:35:30.30,EN,,0,0,0,,So that's the basic operation
Dialogue: 0,0:35:30.59,0:35:33.52,EN,,0,0,0,,for sticking together these bits of code fragments,
Dialogue: 0,0:35:33.93,0:35:35.93,EN,,0,0,0,,these bits of instructions into a sequence.
Dialogue: 0,0:35:36.89,0:35:38.87,EN,,0,0,0,,And you see, from this point of view,
Dialogue: 0,0:35:40.94,0:35:45.96,EN,,0,0,0,,the difference between the interpreter and the compiler, in some sense,
Dialogue: 0,0:35:46.82,0:35:49.34,EN,,0,0,0,,is that where the compiler has these preserving notes,
Dialogue: 0,0:35:50.14,0:35:52.22,EN,,0,0,0,,and says, maybe I'll actually generate the
Dialogue: 0,0:35:52.49,0:35:54.22,EN,,0,0,0,,saves and restores and maybe I won't,
Dialogue: 0,0:35:55.19,0:35:57.24,EN,,0,0,0,,the interpreter being maximally pessimistic
Dialogue: 0,0:35:57.28,0:35:58.90,EN,,0,0,0,,always has a save and restore here.
Dialogue: 0,0:36:00.76,0:36:01.93,EN,,0,0,0,,That's the essential difference.
Dialogue: 0,0:36:04.16,0:36:06.05,EN,,0,0,0,,Well, in order to do this, of course,
Dialogue: 0,0:36:06.65,0:36:09.40,EN,,0,0,0,,the compiler needs some theory of
Dialogue: 0,0:36:09.56,0:36:11.96,EN,,0,0,0,,what code sequences need and modifier registers.
Dialogue: 0,0:36:14.26,0:36:17.28,EN,,0,0,0,,So the tiny little fragments that you put in, like
Dialogue: 0,0:36:17.48,0:36:21.00,EN,,0,0,0,,the basic primitive code fragments,
Dialogue: 0,0:36:22.74,0:36:24.59,EN,,0,0,0,,say, what are the operations that you do
Dialogue: 0,0:36:24.92,0:36:26.04,EN,,0,0,0,,when you look up a variable?
Dialogue: 0,0:36:26.89,0:36:29.02,EN,,0,0,0,,What are the sequence of things that you do
Dialogue: 0,0:36:29.05,0:36:30.68,EN,,0,0,0,,when you compile a constant
Dialogue: 0,0:36:30.97,0:36:32.10,EN,,0,0,0,,or apply a function?
Dialogue: 0,0:36:32.97,0:36:34.48,EN,,0,0,0,,Those have little notations in there
Dialogue: 0,0:36:34.67,0:36:36.46,EN,,0,0,0,,about what they need and what they modify.
Dialogue: 0,0:36:38.78,0:36:41.50,EN,,0,0,0,,So the bottom-level data structures--
Dialogue: 0,0:36:42.66,0:36:44.33,EN,,0,0,0,,Well, I'll say this.
Dialogue: 0,0:36:44.39,0:36:47.91,EN,,0,0,0,,A code sequence to the compiler looks like this.
Dialogue: 0,0:36:48.07,0:36:51.42,EN,,0,0,0,,It has the actual sequence of instructions.
Dialogue: 0,0:36:55.67,0:36:56.81,EN,,0,0,0,,And then, along with it,
Dialogue: 0,0:36:57.18,0:37:02.60,EN,,0,0,0,,there's the set of registers modified.
Dialogue: 0,0:37:10.54,0:37:12.60,EN,,0,0,0,,And then there's the set of registers needed.
Dialogue: 0,0:37:20.00,0:37:22.46,EN,,0,0,0,,So that's the information the compiler has
Dialogue: 0,0:37:23.00,0:37:26.41,EN,,0,0,0,,that it draws on in order to be able to do this operation.
Dialogue: 0,0:37:29.30,0:37:31.08,EN,,0,0,0,,And where do those come from? Well.
Dialogue: 0,0:37:32.91,0:37:34.49,EN,,0,0,0,,Well, those come from, you might expect,
Dialogue: 0,0:37:34.51,0:37:35.53,EN,,0,0,0,,for the very primitive ones,
Dialogue: 0,0:37:35.55,0:37:36.84,EN,,0,0,0,,we're going to put them in by hand.
Dialogue: 0,0:37:37.24,0:37:38.86,EN,,0,0,0,,And then, when we combine two sequences,
Dialogue: 0,0:37:38.89,0:37:41.02,EN,,0,0,0,,we'll figure out what these things should be.
Dialogue: 0,0:37:42.16,0:37:44.12,EN,,0,0,0,,So for example, a very primitive one, let's see.
Dialogue: 0,0:37:48.43,0:37:51.40,EN,,0,0,0,,How about doing a register assignment.
Dialogue: 0,0:37:51.77,0:37:53.50,EN,,0,0,0,,So a primitive sequence might say,
Dialogue: 0,0:37:53.52,0:37:56.22,EN,,0,0,0,,oh, it's code fragment.
Dialogue: 0,0:37:56.22,0:38:03.17,EN,,0,0,0,,Its code instruction is assigned to R1, fetch of R2.
Dialogue: 0,0:38:03.17,0:38:04.27,EN,,0,0,0,,So this is an example.
Dialogue: 0,0:38:05.42,0:38:08.52,EN,,0,0,0,,That might be an example of a sequence of instructions.
Dialogue: 0,0:38:08.77,0:38:10.53,EN,,0,0,0,,And along with that, it'll say, Oh
Dialogue: 0,0:38:10.64,0:38:15.76,EN,,0,0,0,,oh, what I need to remember is that that modifies R1
Dialogue: 0,0:38:18.60,0:38:21.16,EN,,0,0,0,,and then it needs R2.
Dialogue: 0,0:38:24.69,0:38:26.99,EN,,0,0,0,,So when you're first building this compiler,
Dialogue: 0,0:38:27.10,0:38:29.35,EN,,0,0,0,,you put in little fragments of stuff like that.
Dialogue: 0,0:38:30.95,0:38:33.20,EN,,0,0,0,,And now, when it combines two sequences,
Dialogue: 0,0:38:36.70,0:38:38.04,EN,,0,0,0,,if I'm going to combine,
Dialogue: 0,0:38:38.92,0:38:41.58,EN,,0,0,0,,let's say, sequence one,
Dialogue: 0,0:38:42.88,0:38:47.16,EN,,0,0,0,,that modifies a bunch of registers M1,
Dialogue: 0,0:38:48.45,0:38:51.42,EN,,0,0,0,,and needs a bunch of registers N1.
Dialogue: 0,0:38:54.85,0:38:59.48,EN,,0,0,0,,And I'm going to combine that with sequence two.
Dialogue: 0,0:39:00.81,0:39:05.96,EN,,0,0,0,,That modifies a bunch of registers M2,
Dialogue: 0,0:39:07.11,0:39:10.00,EN,,0,0,0,,and needs a bunch of registers N2.
Dialogue: 0,0:39:12.44,0:39:14.83,EN,,0,0,0,,Then, well, we can reason it out.
Dialogue: 0,0:39:15.11,0:39:16.32,EN,,0,0,0,,The new code fragment,
Dialogue: 0,0:39:17.18,0:39:21.82,EN,,0,0,0,,sequence one, and-- followed by sequence two,
Dialogue: 0,0:39:24.09,0:39:26.45,EN,,0,0,0,,well, what's it going to modify?
Dialogue: 0,0:39:27.80,0:39:29.18,EN,,0,0,0,,The things that it will modify are the things
Dialogue: 0,0:39:29.20,0:39:32.68,EN,,0,0,0,,that are modified either by sequence one or sequence two.
Dialogue: 0,0:39:34.00,0:39:36.35,EN,,0,0,0,,So the union of these two
Dialogue: 0,0:39:37.68,0:39:39.64,EN,,0,0,0,,sets are what the new thing modifies.
Dialogue: 0,0:39:40.46,0:39:41.79,EN,,0,0,0,,And then you say, well, what is this--
Dialogue: 0,0:39:44.66,0:39:46.41,EN,,0,0,0,,what registers is it going to need?
Dialogue: 0,0:39:47.95,0:39:49.77,EN,,0,0,0,,It's going to need the things that are,
Dialogue: 0,0:39:49.93,0:39:51.85,EN,,0,0,0,,first of all, needed by sequence one.
Dialogue: 0,0:39:52.91,0:39:54.49,EN,,0,0,0,,So what it needs is sequence one.
Dialogue: 0,0:39:55.19,0:39:58.28,EN,,0,0,0,,And then, well, not quite all of the ones
Dialogue: 0,0:39:58.32,0:39:59.61,EN,,0,0,0,,that are needed by sequence two.
Dialogue: 0,0:39:59.75,0:40:03.49,EN,,0,0,0,,What it needs are the ones that are needed by sequence two
Dialogue: 0,0:40:03.88,0:40:06.88,EN,,0,0,0,,that have not been set up by sequence one.
Dialogue: 0,0:40:08.14,0:40:09.72,EN,,0,0,0,,So it's sort of the union of
Dialogue: 0,0:40:11.66,0:40:13.40,EN,,0,0,0,,the things that sequence two needs
Dialogue: 0,0:40:14.51,0:40:18.52,EN,,0,0,0,,minus the ones that sequence one modifies.
Dialogue: 0,0:40:19.31,0:40:20.88,EN,,0,0,0,,Because it worries about setting them up.
Dialogue: 0,0:40:23.95,0:40:26.26,EN,,0,0,0,,So there's the basic structure of the compiler.
Dialogue: 0,0:40:26.70,0:40:29.82,EN,,0,0,0,,The way you do register optimizations is you
Dialogue: 0,0:40:30.22,0:40:32.70,EN,,0,0,0,,you have some strategies for what needs to be preserved.
Dialogue: 0,0:40:34.10,0:40:35.63,EN,,0,0,0,,That depends on a data structure.
Dialogue: 0,0:40:35.72,0:40:38.51,EN,,0,0,0,,Well, it depends on the operation of what it means to put things together.
Dialogue: 0,0:40:39.03,0:40:41.63,EN,,0,0,0,,Preserving something, that depends on knowing
Dialogue: 0,0:40:41.93,0:40:47.28,EN,,0,0,0,,what registers are needed and modified by these code fragments.
Dialogue: 0,0:40:48.75,0:40:51.26,EN,,0,0,0,,That depends on having little data structures,
Dialogue: 0,0:40:51.42,0:40:55.43,EN,,0,0,0,,which say, a code sequence is the actual instructions,
Dialogue: 0,0:40:55.60,0:40:57.33,EN,,0,0,0,,what they modify and what they need.
Dialogue: 0,0:40:57.33,0:40:59.77,EN,,0,0,0,,That comes from, at the primitive level, building it in.
Dialogue: 0,0:40:59.79,0:41:01.36,EN,,0,0,0,,At the primitive level,
Dialogue: 0,0:41:01.37,0:41:02.52,EN,,0,0,0,,it's going to be completely obvious
Dialogue: 0,0:41:03.00,0:41:04.44,EN,,0,0,0,,what something needs and modifies.
Dialogue: 0,0:41:04.82,0:41:05.35,EN,,0,0,0,,Plus,
Dialogue: 0,0:41:05.44,0:41:08.60,EN,,0,0,0,,this particular way that says, when I build up bigger ones,
Dialogue: 0,0:41:09.28,0:41:11.89,EN,,0,0,0,,here's how I generate the new set of registers modified
Dialogue: 0,0:41:11.93,0:41:13.37,EN,,0,0,0,,and the new set of registers needed.
Dialogue: 0,0:41:15.27,0:41:17.77,EN,,0,0,0,,And that's the whole-- well, I shouldn't say that's the whole thing.
Dialogue: 0,0:41:17.77,0:41:19.34,EN,,0,0,0,,That's the whole thing except for about
Dialogue: 0,0:41:19.74,0:41:21.87,EN,,0,0,0,,about 30 pages of details in the book.
Dialogue: 0,0:41:22.31,0:41:27.69,EN,,0,0,0,,But it is a perfectly usable rudimentary compiler.
Dialogue: 0,0:41:28.76,0:41:31.37,EN,,0,0,0,,Let me kind of show you what it does.
Dialogue: 0,0:41:31.39,0:41:35.56,EN,,0,0,0,,Suppose we start out with recursive factorial.
Dialogue: 0,0:41:36.20,0:41:38.60,EN,,0,0,0,,And these slides are going to be much too small to read.
Dialogue: 0,0:41:38.60,0:41:39.79,EN,,0,0,0,,I just want to flash through the code
Dialogue: 0,0:41:39.79,0:41:41.28,EN,,0,0,0,,and show you about how much it is.
Dialogue: 0,0:41:42.25,0:41:43.29,EN,,0,0,0,,That starts out with--
Dialogue: 0,0:41:44.32,0:41:45.68,EN,,0,0,0,,here's a first block of it,
Dialogue: 0,0:41:45.95,0:41:47.68,EN,,0,0,0,,where it compiles a procedure entry
Dialogue: 0,0:41:47.69,0:41:48.73,EN,,0,0,0,,and does a bunch of assignments.
Dialogue: 0,0:41:48.75,0:41:51.48,EN,,0,0,0,,And this thing is basically up through the part where
Dialogue: 0,0:41:52.65,0:41:53.90,EN,,0,0,0,,sets up to do the predicate
Dialogue: 0,0:41:54.31,0:41:56.59,EN,,0,0,0,,and test whether the predicate's true.
Dialogue: 0,0:41:56.97,0:41:57.85,EN,,0,0,0,,The second part
Dialogue: 0,0:41:58.46,0:42:03.73,EN,,0,0,0,,is what results from-- in the recursive call to fact of n minus one.
Dialogue: 0,0:42:04.12,0:42:05.05,EN,,0,0,0,,And this last part
Dialogue: 0,0:42:06.07,0:42:07.48,EN,,0,0,0,,is coming back from that
Dialogue: 0,0:42:07.87,0:42:09.90,EN,,0,0,0,,and then taking care of the constant case.
Dialogue: 0,0:42:09.90,0:42:13.16,EN,,0,0,0,,So that's about how much code it would produce for factorial.
Dialogue: 0,0:42:13.72,0:42:17.69,EN,,0,0,0,,We could make this compiler much, much better, of course.
Dialogue: 0,0:42:18.67,0:42:21.24,EN,,0,0,0,,The main way we could make it better is
Dialogue: 0,0:42:21.24,0:42:24.00,EN,,0,0,0,,to allow the compiler to make any assumptions at all
Dialogue: 0,0:42:24.35,0:42:26.27,EN,,0,0,0,,about what happens when you call a procedure.
Dialogue: 0,0:42:26.97,0:42:28.28,EN,,0,0,0,,So this compiler, for instance,
Dialogue: 0,0:42:28.30,0:42:32.32,EN,,0,0,0,,doesn't even know, say, that multiplication
Dialogue: 0,0:42:33.12,0:42:36.14,EN,,0,0,0,,you say, is something that could be coded in line.
Dialogue: 0,0:42:36.14,0:42:37.87,EN,,0,0,0,,Instead, it sets up this whole mechanism.
Dialogue: 0,0:42:38.00,0:42:39.34,EN,,0,0,0,,It goes to apply-dispatch.
Dialogue: 0,0:42:41.37,0:42:42.49,EN,,0,0,0,,That's a tremendous waste,
Dialogue: 0,0:42:42.54,0:42:45.02,EN,,0,0,0,,because what you do every time you go to apply-dispatch
Dialogue: 0,0:42:45.02,0:42:46.80,EN,,0,0,0,,is you have to concern about this argument list,
Dialogue: 0,0:42:47.40,0:42:49.10,EN,,0,0,0,,because it's a very general thing you're going to.
Dialogue: 0,0:42:49.13,0:42:51.07,EN,,0,0,0,,In any real compiler, of course,
Dialogue: 0,0:42:51.08,0:42:53.29,EN,,0,0,0,,you're going to have registers for holding arguments.
Dialogue: 0,0:42:53.77,0:42:55.31,EN,,0,0,0,,And you're going to start preserving
Dialogue: 0,0:42:56.38,0:42:58.05,EN,,0,0,0,,saving the way you use those registers
Dialogue: 0,0:42:58.05,0:43:01.61,EN,,0,0,0,,similar to the same strategy here.
Dialogue: 0,0:43:02.85,0:43:05.93,EN,,0,0,0,,So that's probably the very main way
Dialogue: 0,0:43:05.95,0:43:08.30,EN,,0,0,0,,this particular compiler in the book could be fixed.
Dialogue: 0,0:43:08.69,0:43:09.70,EN,,0,0,0,,There are other things like
Dialogue: 0,0:43:09.70,0:43:11.82,EN,,0,0,0,,looking up variable values and
Dialogue: 0,0:43:11.83,0:43:13.87,EN,,0,0,0,,making more efficient primitive operations,
Dialogue: 0,0:43:13.88,0:43:14.56,EN,,0,0,0,,and all sorts of things.
Dialogue: 0,0:43:14.59,0:43:16.60,EN,,0,0,0,,Essentially, a good Lisp compiler
Dialogue: 0,0:43:16.62,0:43:18.49,EN,,0,0,0,,can absorb an arbitrary amount of effort.
Dialogue: 0,0:43:19.72,0:43:21.63,EN,,0,0,0,,And probably one of the reasons
Dialogue: 0,0:43:21.89,0:43:23.04,EN,,0,0,0,,Lisp is slow
Dialogue: 0,0:43:23.63,0:43:25.44,EN,,0,0,0,,with compared to languages like FORTRAN
Dialogue: 0,0:43:25.90,0:43:28.19,EN,,0,0,0,,is that, if you look over history
Dialogue: 0,0:43:28.22,0:43:31.12,EN,,0,0,0,,the amount of effort that's gone into building Lisp compilers,
Dialogue: 0,0:43:31.16,0:43:32.35,EN,,0,0,0,,it's nowhere near the amount of effort
Dialogue: 0,0:43:32.36,0:43:33.90,EN,,0,0,0,,that's gone into FORTRAN compilers.
Dialogue: 0,0:43:34.43,0:43:35.79,EN,,0,0,0,,And maybe that's something that will
Dialogue: 0,0:43:35.92,0:43:37.68,EN,,0,0,0,,that will change over the next couple of years.
Dialogue: 0,0:43:38.00,0:43:38.83,EN,,0,0,0,,OK, let's break.
Dialogue: 0,0:43:43.80,0:43:44.65,EN,,0,0,0,,Questions?
Dialogue: 0,0:43:48.27,0:43:49.95,EN,,0,0,0,,AUDIENCE: One of the very first classes--
Dialogue: 0,0:43:49.95,0:43:51.40,EN,,0,0,0,,I don't know if it was during class or after class-
Dialogue: 0,0:43:51.47,0:43:53.88,EN,,0,0,0,,you showed me the, the
Dialogue: 0,0:43:54.00,0:43:57.52,EN,,0,0,0,,say, addition has a primitive that we don't see,
Dialogue: 0,0:43:57.69,0:43:59.21,EN,,0,0,0,,and-percent add or something like that.
Dialogue: 0,0:43:59.82,0:44:01.65,EN,,0,0,0,,Is that because,
Dialogue: 0,0:44:01.65,0:44:02.60,EN,,0,0,0,,if you're doing inline code
Dialogue: 0,0:44:02.60,0:44:08.19,EN,,0,0,0,,you'd want to just do it for two operators, operands?
Dialogue: 0,0:44:08.70,0:44:10.25,EN,,0,0,0,,But if you had more operands,
Dialogue: 0,0:44:10.28,0:44:11.47,EN,,0,0,0,,you'd want to do something special?
Dialogue: 0,0:44:12.71,0:44:16.04,EN,,0,0,0,,PROFESSOR: Yeah, you're looking in the actual scheme implementation.
Dialogue: 0,0:44:16.06,0:44:17.84,EN,,0,0,0,,There's a plus, and a plus is some operator.
Dialogue: 0,0:44:17.90,0:44:20.19,EN,,0,0,0,,And then if you go look inside the code for plus,
Dialogue: 0,0:44:20.33,0:44:21.37,EN,,0,0,0,,you see something called--
Dialogue: 0,0:44:21.57,0:44:24.14,EN,,0,0,0,,I forget-- and-percent plus or something like that.
Dialogue: 0,0:44:24.55,0:44:25.79,EN,,0,0,0,,And what's going on there is
Dialogue: 0,0:44:25.79,0:44:27.92,EN,,0,0,0,,is that particular kind of optimization.
Dialogue: 0,0:44:28.47,0:44:31.87,EN,,0,0,0,,Because, see, general plus takes an arbitrary number of arguments.
Dialogue: 0,0:44:35.02,0:44:36.38,EN,,0,0,0,,So the most general plus
Dialogue: 0,0:44:36.76,0:44:38.25,EN,,0,0,0,,says, oh, if I have an argument list,
Dialogue: 0,0:44:38.28,0:44:40.62,EN,,0,0,0,,I'd better cons it up in some list
Dialogue: 0,0:44:41.63,0:44:44.14,EN,,0,0,0,,and then figure out how many there were or something like that.
Dialogue: 0,0:44:44.72,0:44:46.16,EN,,0,0,0,,That's terribly inefficient,
Dialogue: 0,0:44:46.81,0:44:49.25,EN,,0,0,0,,especially since most of the time you're probably adding two numbers.
Dialogue: 0,0:44:49.25,0:44:51.24,EN,,0,0,0,,You don't want to really have to cons this argument list.
Dialogue: 0,0:44:52.04,0:44:53.93,EN,,0,0,0,,So what you'd like to do is build
Dialogue: 0,0:44:55.66,0:44:57.71,EN,,0,0,0,,the code for plus with a bunch of entries.
Dialogue: 0,0:44:58.15,0:45:00.17,EN,,0,0,0,,So most of what it's doing is the same.
Dialogue: 0,0:45:00.49,0:45:01.95,EN,,0,0,0,,However, there might be a special entry
Dialogue: 0,0:45:01.98,0:45:03.92,EN,,0,0,0,,that you'd go to if you knew there were only two arguments.
Dialogue: 0,0:45:04.56,0:45:05.87,EN,,0,0,0,,And those you'll put in registers.
Dialogue: 0,0:45:05.87,0:45:06.97,EN,,0,0,0,,they won't be in an argument list
Dialogue: 0,0:45:06.99,0:45:07.98,EN,,0,0,0,,and you won't have to CONS.
Dialogue: 0,0:45:08.67,0:45:10.42,EN,,0,0,0,,That's how a lot of these things work.
Dialogue: 0,0:45:12.30,0:45:13.72,EN,,0,0,0,,OK, let's take a break.
Dialogue: 0,0:00:00.01,0:00:05.02,Declare,,0,0,0,,{\an2\fad(500,500)}Learning-SICP学习小组\N倾情制作
Dialogue: 0,0:00:05.37,0:00:11.84,staff,,0,0,0,,{\fad(600,800)\pos(110.666,403.334)}翻译&&时间轴\N杨启钊\N(windfarer)
Dialogue: 0,0:00:05.37,0:00:11.84,staff,,0,0,0,,{\fad(600,800)\pos(534.666,404)}压制&&特效\N邓雄飞\N(Dysprosium)
Dialogue: 0,0:00:05.37,0:00:11.84,staff,,0,0,0,,{\fad(600,800)\pos(574.667,277.333)}校对\N邓雄飞
Dialogue: 0,0:00:05.37,0:00:11.84,staff,,0,0,0,,{\fad(600,800)\pos(89.334,273.333)}特别感谢\N裘宗燕教授
Dialogue: 0,0:00:05.37,0:00:11.84,title,,0,0,0,,{\fad(600,800)\pos(324,32)}《计算机程序的构造和解释》
Dialogue: 0,0:00:11.84,0:00:13.84,Declare,,0,0,0,,{\an2\fad(500,500)}编译
Dialogue: 0,0:00:19.36,0:00:22.65,Default,,0,0,0,,教授: 上节课 我们学习了
Dialogue: 0,0:00:22.65,0:00:25.67,Default,,0,0,0,,一个显式控制的Lisp求值器
Dialogue: 0,0:00:25.67,0:00:28.97,Default,,0,0,0,,它弥合了像Lisp
Dialogue: 0,0:00:29.05,0:00:32.14,Default,,0,0,0,,或者查询语言之类的高级语言
Dialogue: 0,0:00:32.50,0:00:36.16,Default,,0,0,0,,与传统寄存器机器之间的鸿沟
Dialogue: 0,0:00:36.70,0:00:40.14,Default,,0,0,0,,事实上 你可以将显式控制求值器
Dialogue: 0,0:00:40.16,0:00:44.38,Default,,0,0,0,,看作是 在一台常见的
Dialogue: 0,0:00:44.40,0:00:45.95,Default,,0,0,0,,寄存机器上实现的
Dialogue: 0,0:00:46.52,0:00:49.50,Default,,0,0,0,,Lisp求值器的汇编代码
Dialogue: 0,0:00:49.50,0:00:51.50,Default,,0,0,0,,或者 你可以把它看做是
Dialogue: 0,0:00:52.08,0:00:54.56,Default,,0,0,0,,某台专门运行Lisp的机器的微程序
Dialogue: 0,0:00:55.20,0:00:55.92,Default,,0,0,0,,无论是那种情况
Dialogue: 0,0:00:55.92,0:00:58.68,Default,,0,0,0,,我们都是把一台
Dialogue: 0,0:00:58.94,0:01:00.51,Default,,0,0,0,,处理低级语言的机器
Dialogue: 0,0:01:01.42,0:01:03.32,Default,,0,0,0,,抬高到一个层次
Dialogue: 0,0:01:03.37,0:01:04.88,Default,,0,0,0,,以便处理像Lisp这样的高级语言
Dialogue: 0,0:01:05.36,0:01:06.35,Default,,0,0,0,,这是通过编写解释器来实现的
Dialogue: 0,0:01:08.22,0:01:09.58,Default,,0,0,0,,来看个例子
Dialogue: 0,0:01:11.82,0:01:13.77,Default,,0,0,0,,这里 从概念上来说
Dialogue: 0,0:01:18.01,0:01:19.47,Default,,0,0,0,,从概念上来说 这是一台
Dialogue: 0,0:01:20.54,0:01:23.44,Default,,0,0,0,,专用于计算阶乘的机器
Dialogue: 0,0:01:24.09,0:01:27.39,Default,,0,0,0,,输入5 输出120
Dialogue: 0,0:01:28.92,0:01:30.83,Default,,0,0,0,,这个专用机器实际上
Dialogue: 0,0:01:30.97,0:01:32.72,Default,,0,0,0,,是一个Lisp解释器
Dialogue: 0,0:01:33.50,0:01:36.17,Default,,0,0,0,,它将自己配置为计算阶乘
Dialogue: 0,0:01:38.35,0:01:40.99,Default,,0,0,0,,因为你向它送入了一台阶乘机器的描述
Dialogue: 0,0:01:42.12,0:01:43.70,Default,,0,0,0,,这就是解释器
Dialogue: 0,0:01:43.70,0:01:45.66,Default,,0,0,0,,它将自己配置为
Dialogue: 0,0:01:46.37,0:01:49.24,Default,,0,0,0,,模拟你所输入描述的机器
Dialogue: 0,0:01:50.07,0:01:51.93,Default,,0,0,0,,那么 在Lisp解释器里是什么?
Dialogue: 0,0:01:52.04,0:01:55.44,Default,,0,0,0,,里面可能是通用的寄存器语言解释器
Dialogue: 0,0:01:56.98,0:02:00.18,Default,,0,0,0,,它将自己配置成像Lisp解释器那样
Dialogue: 0,0:02:00.18,0:02:02.03,Default,,0,0,0,,因为你输入了一系列用寄存器语言
Dialogue: 0,0:02:02.12,0:02:03.04,Default,,0,0,0,,编写的指令
Dialogue: 0,0:02:03.37,0:02:05.16,Default,,0,0,0,,这就是显式控制求值器
Dialogue: 0,0:02:07.05,0:02:08.70,Default,,0,0,0,,它里面也有一些库
Dialogue: 0,0:02:08.73,0:02:11.08,Default,,0,0,0,,由基本运算符和Lisp运算
Dialogue: 0,0:02:11.12,0:02:12.28,Default,,0,0,0,,等等要素组成
Dialogue: 0,0:02:12.75,0:02:16.89,Default,,0,0,0,,这是解释执行的一般策略
Dialogue: 0,0:02:17.32,0:02:18.51,Default,,0,0,0,,事实上 我们所做的是
Dialogue: 0,0:02:18.60,0:02:20.14,Default,,0,0,0,,通过编写解释器
Dialogue: 0,0:02:21.62,0:02:23.40,Default,,0,0,0,,将机器抬升到
Dialogue: 0,0:02:23.42,0:02:25.24,Default,,0,0,0,,我们程序所在的层次
Dialogue: 0,0:02:25.24,0:02:26.72,Default,,0,0,0,,当然 还有另外一种策略
Dialogue: 0,0:02:27.42,0:02:28.89,Default,,0,0,0,,这种不同的策略就是编译
Dialogue: 0,0:02:29.04,0:02:30.43,Default,,0,0,0,,编译有一些不同
Dialogue: 0,0:02:31.04,0:02:31.50,Default,,0,0,0,,这里
Dialogue: 0,0:02:33.37,0:02:34.75,Default,,0,0,0,,我们可能已经实现了
Dialogue: 0,0:02:35.67,0:02:38.52,Default,,0,0,0,,一个特定用途的机器
Dialogue: 0,0:02:38.62,0:02:39.98,Default,,0,0,0,,用来计算阶乘
Dialogue: 0,0:02:43.62,0:02:46.26,Default,,0,0,0,,从某种使用寄存器语言的机器开始
Dialogue: 0,0:02:46.26,0:02:47.72,Default,,0,0,0,,但是 我们将让它执行不同的策略
Dialogue: 0,0:02:47.72,0:02:50.38,Default,,0,0,0,,把我们的阶乘程序
Dialogue: 0,0:02:51.55,0:02:53.92,Default,,0,0,0,,作为源代码输入编译器
Dialogue: 0,0:02:53.92,0:02:55.15,Default,,0,0,0,,编译器就会
Dialogue: 0,0:02:55.15,0:02:57.62,Default,,0,0,0,,把这个阶乘程序
Dialogue: 0,0:02:57.62,0:02:59.07,Default,,0,0,0,,翻译成某种寄存器机器语言
Dialogue: 0,0:03:00.25,0:03:03.40,Default,,0,0,0,,现在它并不是Lisp的显式控制求值器
Dialogue: 0,0:03:03.40,0:03:06.17,Default,,0,0,0,,而是某种用来计算阶乘的寄存器语言
Dialogue: 0,0:03:06.49,0:03:08.36,Default,,0,0,0,,这就是翻译的过程
Dialogue: 0,0:03:10.54,0:03:12.41,Default,,0,0,0,,它将进入某种加载器
Dialogue: 0,0:03:13.35,0:03:15.21,Default,,0,0,0,,它会把这些代码
Dialogue: 0,0:03:15.31,0:03:16.84,Default,,0,0,0,,和从程序库中选取的代码
Dialogue: 0,0:03:16.86,0:03:18.65,Default,,0,0,0,,比如乘法运算等 结合在一起
Dialogue: 0,0:03:19.82,0:03:21.69,Default,,0,0,0,,随后我们将生成一个加载模块
Dialogue: 0,0:03:22.22,0:03:25.06,Default,,0,0,0,,它把寄存器语言机器配置成
Dialogue: 0,0:03:25.06,0:03:27.24,Default,,0,0,0,,一个专门用来计算阶乘的机器
Dialogue: 0,0:03:28.12,0:03:30.22,Default,,0,0,0,,这就是不同的策略
Dialogue: 0,0:03:30.22,0:03:31.22,Default,,0,0,0,,在解释中
Dialogue: 0,0:03:31.22,0:03:32.01,Default,,0,0,0,,我们把机器
Dialogue: 0,0:03:32.91,0:03:35.23,Default,,0,0,0,,抬升到Lisp语言的层次
Dialogue: 0,0:03:35.32,0:03:36.34,Default,,0,0,0,,而在编译中
Dialogue: 0,0:03:36.34,0:03:38.43,Default,,0,0,0,,我们将我们的程序下降到
Dialogue: 0,0:03:38.48,0:03:40.56,Default,,0,0,0,,机器语言的层次
Dialogue: 0,0:03:41.96,0:03:43.84,Default,,0,0,0,,那么 这两个策略有什么区别呢?
Dialogue: 0,0:03:44.30,0:03:49.42,Default,,0,0,0,,编译器可以生成执行起来更有效率的代码
Dialogue: 0,0:03:52.05,0:03:53.90,Default,,0,0,0,,主要原因是
Dialogue: 0,0:03:54.17,0:03:58.89,Default,,0,0,0,,如果你考虑运行中的寄存器操作
Dialogue: 0,0:04:01.92,0:04:04.49,Default,,0,0,0,,解释器需要生成寄存器的操作
Dialogue: 0,0:04:04.97,0:04:06.75,Default,,0,0,0,,从原则上来讲 它需要足够通用
Dialogue: 0,0:04:07.32,0:04:08.94,Default,,0,0,0,,以支持任何Lisp过程的执行
Dialogue: 0,0:04:10.22,0:04:12.25,Default,,0,0,0,,而编译器只需要
Dialogue: 0,0:04:12.27,0:04:14.92,Default,,0,0,0,,生成一组特定的寄存器操作
Dialogue: 0,0:04:15.52,0:04:18.22,Default,,0,0,0,,用来执行你所编译的那部分特定的Lisp过程
Dialogue: 0,0:04:20.17,0:04:21.20,Default,,0,0,0,,换一种说法
Dialogue: 0,0:04:21.20,0:04:25.31,Default,,0,0,0,,解释器是一种通用的模拟器
Dialogue: 0,0:04:25.92,0:04:27.58,Default,,0,0,0,,当你输入一个Lisp过程时
Dialogue: 0,0:04:27.58,0:04:31.32,Default,,0,0,0,,它们就会模拟那个过程所描述的程序
Dialogue: 0,0:04:31.32,0:04:33.87,Default,,0,0,0,,所以解释器旨在成为一个通用模拟器
Dialogue: 0,0:04:34.62,0:04:35.96,Default,,0,0,0,,而编译器 实际上
Dialogue: 0,0:04:36.00,0:04:37.68,Default,,0,0,0,,只需要将东西配置成
Dialogue: 0,0:04:37.71,0:04:39.34,Default,,0,0,0,,解释器将要去模拟的机器
Dialogue: 0,0:04:40.02,0:04:41.34,Default,,0,0,0,,所以编译器可以运行得更快
Dialogue: 0,0:04:52.55,0:04:53.64,Default,,0,0,0,,另一方面
Dialogue: 0,0:04:55.97,0:04:58.28,Default,,0,0,0,,解释器更适合用来排查错误
Dialogue: 0,0:04:59.43,0:05:01.25,Default,,0,0,0,,这是因为
Dialogue: 0,0:05:01.57,0:05:03.02,Default,,0,0,0,,我们的源代码实际上就在那里
Dialogue: 0,0:05:03.02,0:05:04.81,Default,,0,0,0,,我们正在解释它们
Dialogue: 0,0:05:05.87,0:05:07.69,Default,,0,0,0,,并且库也在其中
Dialogue: 0,0:05:07.90,0:05:10.89,Default,,0,0,0,,看 库是解释器的一部分
Dialogue: 0,0:05:11.30,0:05:13.16,Default,,0,0,0,,而编译器只会拉取
Dialogue: 0,0:05:13.20,0:05:14.56,Default,,0,0,0,,运行程序所需要的代码
Dialogue: 0,0:05:14.87,0:05:17.00,Default,,0,0,0,,所以 如果你在排查错误的途中
Dialogue: 0,0:05:18.00,0:05:20.72,Default,,0,0,0,,你想写一些额外的代码
Dialogue: 0,0:05:20.80,0:05:22.57,Default,,0,0,0,,来考察运行过程中的数据类型
Dialogue: 0,0:05:23.05,0:05:24.25,Default,,0,0,0,,或者做一些
Dialogue: 0,0:05:24.30,0:05:25.92,Default,,0,0,0,,在写程序时没有想到的计算
Dialogue: 0,0:05:25.95,0:05:27.53,Default,,0,0,0,,解释器可以完美搞定这些
Dialogue: 0,0:05:28.05,0:05:29.21,Default,,0,0,0,,而编译器不行
Dialogue: 0,0:05:29.62,0:05:31.90,Default,,0,0,0,,所以它们各有优点
Dialogue: 0,0:05:31.90,0:05:34.48,Default,,0,0,0,,编译器将生成运行更快的代码
Dialogue: 0,0:05:34.85,0:05:37.02,Default,,0,0,0,,而解释器是一种更适合排错的环境
Dialogue: 0,0:05:38.95,0:05:41.40,Default,,0,0,0,,大多数Lisp系统最终将二者都实现了
Dialogue: 0,0:05:42.92,0:05:45.23,Default,,0,0,0,,这样你就可以在开发阶段
Dialogue: 0,0:05:45.24,0:05:47.08,Default,,0,0,0,,可以使用解释器
Dialogue: 0,0:05:47.08,0:05:48.62,Default,,0,0,0,,随后通过编译加速代码的运行
Dialogue: 0,0:05:49.02,0:05:50.03,Default,,0,0,0,,并且通常
Dialogue: 0,0:05:50.04,0:05:51.68,Default,,0,0,0,,你能够让被编译的代码
Dialogue: 0,0:05:51.69,0:05:53.56,Default,,0,0,0,,和被解释的代码互相调用
Dialogue: 0,0:05:54.60,0:05:56.33,Default,,0,0,0,,我们将学习如何做到 其实不难
Dialogue: 0,0:05:59.27,0:05:59.85,Default,,0,0,0,,好
Dialogue: 0,0:06:00.97,0:06:02.09,Default,,0,0,0,,事实上
Dialogue: 0,0:06:04.30,0:06:05.75,Default,,0,0,0,,在我们将要构建的编译器中
Dialogue: 0,0:06:05.75,0:06:07.58,Default,,0,0,0,,我们实现编译的代码和解释的代码
Dialogue: 0,0:06:07.58,0:06:09.45,Default,,0,0,0,,互相调用的方式是
Dialogue: 0,0:06:09.90,0:06:12.06,Default,,0,0,0,,我们让编译器和解释器使用
Dialogue: 0,0:06:12.11,0:06:14.40,Default,,0,0,0,,使用完全一致的寄存器约定
Dialogue: 0,0:06:18.42,0:06:21.72,Default,,0,0,0,,编译器的理念
Dialogue: 0,0:06:21.76,0:06:25.74,Default,,0,0,0,,与解释器或求值器的理念很像
Dialogue: 0,0:06:25.87,0:06:26.46,Default,,0,0,0,,它们是相同的
Dialogue: 0,0:06:27.05,0:06:29.39,Default,,0,0,0,,求值器遍历代码
Dialogue: 0,0:06:29.82,0:06:32.35,Default,,0,0,0,,产生一些寄存器操作
Dialogue: 0,0:06:33.65,0:06:34.97,Default,,0,0,0,,就是我们昨天做的事情
Dialogue: 0,0:06:37.10,0:06:40.27,Default,,0,0,0,,而编译器会读取代码
Dialogue: 0,0:06:40.52,0:06:43.00,Default,,0,0,0,,生成一些进行求值时
Dialogue: 0,0:06:43.04,0:06:44.67,Default,,0,0,0,,求值器会进行的
Dialogue: 0,0:06:45.23,0:06:46.64,Default,,0,0,0,,相关寄存器操作
Dialogue: 0,0:06:48.60,0:06:49.95,Default,,0,0,0,,这就给我们提供了一个模型
Dialogue: 0,0:06:50.60,0:06:53.77,Default,,0,0,0,,来实现一个零阶编译器
Dialogue: 0,0:06:55.30,0:06:58.32,Default,,0,0,0,,一个很差劲但是能用的编译器
Dialogue: 0,0:06:58.32,0:06:59.32,Default,,0,0,0,,这种模型就是
Dialogue: 0,0:06:59.36,0:07:00.59,Default,,0,0,0,,你用求值器
Dialogue: 0,0:07:00.68,0:07:01.88,Default,,0,0,0,,把代码跑一遍
Dialogue: 0,0:07:02.80,0:07:06.06,Default,,0,0,0,,但不去执行实际的操作
Dialogue: 0,0:07:06.06,0:07:07.15,Default,,0,0,0,,只是把它们保存下来
Dialogue: 0,0:07:07.55,0:07:08.82,Default,,0,0,0,,那就是你编译后的代码
Dialogue: 0,0:07:08.82,0:07:10.24,Default,,0,0,0,,让我举个例子
Dialogue: 0,0:07:12.70,0:07:14.14,Default,,0,0,0,,假设我们要编译
Dialogue: 0,0:07:15.10,0:07:17.90,Default,,0,0,0,,编译(F X) 这个表达式
Dialogue: 0,0:07:25.07,0:07:25.96,Default,,0,0,0,,我们假设
Dialogue: 0,0:07:25.96,0:07:28.06,Default,,0,0,0,,EXP寄存器中保存着(F X)
Dialogue: 0,0:07:28.06,0:07:29.55,Default,,0,0,0,,而ENV寄存器又保存着其它东西
Dialogue: 0,0:07:30.10,0:07:32.20,Default,,0,0,0,,想象我们启动了求值器
Dialogue: 0,0:07:34.60,0:07:35.71,Default,,0,0,0,,它读取了表达式
Dialogue: 0,0:07:35.71,0:07:37.36,Default,,0,0,0,,判断它是一个应用
Dialogue: 0,0:07:37.92,0:07:41.90,Default,,0,0,0,,它分支到求值器代码中的一个地方
Dialogue: 0,0:07:42.52,0:07:45.15,Default,,0,0,0,,我们之前见过的叫EV-APPLICATION的地方
Dialogue: 0,0:07:47.12,0:07:48.12,Default,,0,0,0,,然后继续处理
Dialogue: 0,0:07:48.16,0:07:50.08,Default,,0,0,0,,恢复运算对象和UNEV
Dialogue: 0,0:07:50.08,0:07:52.44,Default,,0,0,0,,然后之后它将运算符放在EXP寄存器中
Dialogue: 0,0:07:52.48,0:07:54.27,Default,,0,0,0,,递归地对它求值
Dialogue: 0,0:07:54.47,0:07:56.08,Default,,0,0,0,,这就是我们经历的过程
Dialogue: 0,0:07:56.67,0:07:57.84,Default,,0,0,0,,如果你看代码
Dialogue: 0,0:07:57.87,0:07:59.74,Default,,0,0,0,,会看到一些寄存器操作
Dialogue: 0,0:08:00.20,0:08:02.30,Default,,0,0,0,,你会看到将运算对象赋值给UNEV寄存器
Dialogue: 0,0:08:02.30,0:08:03.95,Default,,0,0,0,,把运算符赋值给EXP
Dialogue: 0,0:08:04.09,0:08:06.20,Default,,0,0,0,,保存环境、生成新环境 等等
Dialogue: 0,0:08:10.22,0:08:11.93,Default,,0,0,0,,如果我们来看下这里的投影
Dialogue: 0,0:08:15.75,0:08:19.58,Default,,0,0,0,,我们会看到产生的这些操作
Dialogue: 0,0:08:20.82,0:08:22.52,Default,,0,0,0,,这是求值器实际要进行的
Dialogue: 0,0:08:22.72,0:08:24.80,Default,,0,0,0,,第一个操作
Dialogue: 0,0:08:25.00,0:08:27.20,Default,,0,0,0,,它将运算对象从EXP寄存器里取出来
Dialogue: 0,0:08:27.47,0:08:28.62,Default,,0,0,0,,并将它赋值给UNEV
Dialogue: 0,0:08:30.03,0:08:32.27,Default,,0,0,0,,然后它给EXP寄存器赋了某个值
Dialogue: 0,0:08:32.30,0:08:33.46,Default,,0,0,0,,然后保存CONTINUE
Dialogue: 0,0:08:33.46,0:08:34.62,Default,,0,0,0,,保存ENV
Dialogue: 0,0:08:34.62,0:08:38.65,Default,,0,0,0,,我在这里就只是寄存器赋值
Dialogue: 0,0:08:39.57,0:08:42.32,Default,,0,0,0,,这就是求值器求值代码时进行的操作
Dialogue: 0,0:08:42.77,0:08:43.79,Default,,0,0,0,,我们缩小画面看看
Dialogue: 0,0:08:44.30,0:08:47.13,Default,,0,0,0,,总计有19个操作
Dialogue: 0,0:08:49.40,0:08:51.64,Default,,0,0,0,,这些代码
Dialogue: 0,0:08:52.05,0:08:53.90,Default,,0,0,0,,对应着
Dialogue: 0,0:08:54.75,0:08:57.10,Default,,0,0,0,,求值器跳转到APPLY-DISPATCH代码之前
Dialogue: 0,0:08:57.86,0:08:59.16,Default,,0,0,0,,事实上 在这个编译器中
Dialogue: 0,0:08:59.20,0:09:01.18,Default,,0,0,0,,我们不需要再关心APPLY-DISPATCH了
Dialogue: 0,0:09:01.30,0:09:02.11,Default,,0,0,0,,我们有所有东西
Dialogue: 0,0:09:02.35,0:09:05.04,Default,,0,0,0,,我们拥有解释后和编译后的所有代码
Dialogue: 0,0:09:06.07,0:09:07.61,Default,,0,0,0,,通常求值过程
Dialogue: 0,0:09:07.61,0:09:09.85,Default,,0,0,0,,是由APPLY-DISPATCH处理的
Dialogue: 0,0:09:10.27,0:09:12.32,Default,,0,0,0,,这将让被解释后代码与编译后代码
Dialogue: 0,0:09:12.36,0:09:13.71,Default,,0,0,0,,很容易互相调用
Dialogue: 0,0:09:18.27,0:09:19.87,Default,,0,0,0,,从原理上来说 这样做足矣
Dialogue: 0,0:09:21.05,0:09:22.66,Default,,0,0,0,,只需运行求值器
Dialogue: 0,0:09:22.66,0:09:24.50,Default,,0,0,0,,因而编译器非常像求值器
Dialogue: 0,0:09:24.50,0:09:26.47,Default,,0,0,0,,你运行它 唯一不同是你把操作存下来
Dialogue: 0,0:09:26.47,0:09:28.40,Default,,0,0,0,,而不是实际执行它们
Dialogue: 0,0:09:29.35,0:09:31.39,Default,,0,0,0,,这其实不完全正确
Dialogue: 0,0:09:32.91,0:09:34.99,Default,,0,0,0,,这里面我们撒了个小谎
Dialogue: 0,0:09:36.24,0:09:39.29,Default,,0,0,0,,你需要关心的是:如果有个谓词
Dialogue: 0,0:09:40.12,0:09:42.16,Default,,0,0,0,,如果你要进行某种测试
Dialogue: 0,0:09:43.45,0:09:46.03,Default,,0,0,0,,显然 在你编译时
Dialogue: 0,0:09:46.52,0:09:47.98,Default,,0,0,0,,你不知道这些分支中
Dialogue: 0,0:09:48.32,0:09:50.14,Default,,0,0,0,,哪条分支会被执行
Dialogue: 0,0:09:51.13,0:09:53.92,Default,,0,0,0,,所以你不能确定求值器将对哪个求值
Dialogue: 0,0:09:54.90,0:09:57.12,Default,,0,0,0,,因此在这里就很简单
Dialogue: 0,0:09:57.12,0:09:58.49,Default,,0,0,0,,你把两个分支全编译了
Dialogue: 0,0:09:59.32,0:10:01.29,Default,,0,0,0,,因此你编译出一个这样的结构
Dialogue: 0,0:10:02.00,0:10:03.98,Default,,0,0,0,,它们都会被编译成
Dialogue: 0,0:10:05.31,0:10:09.15,Default,,0,0,0,,首先是P的代码
Dialogue: 0,0:10:10.71,0:10:16.51,Default,,0,0,0,,它把结果存入VAL寄存器
Dialogue: 0,0:10:18.17,0:10:20.64,Default,,0,0,0,,解释器对谓词求值
Dialogue: 0,0:10:21.35,0:10:24.19,Default,,0,0,0,,并保证结果会放到VAL寄存器中
Dialogue: 0,0:10:24.70,0:10:27.22,Default,,0,0,0,,随后你编译一条指令
Dialogue: 0,0:10:27.22,0:10:33.79,Default,,0,0,0,,如果VAL是TRUE
Dialogue: 0,0:10:37.17,0:10:38.75,Default,,0,0,0,,就转到LABEL1这个地方
Dialogue: 0,0:10:44.97,0:10:47.52,Default,,0,0,0,,然后我们写下B的代码
Dialogue: 0,0:10:49.42,0:10:52.32,Default,,0,0,0,,让解释器对B进行求值
Dialogue: 0,0:10:53.62,0:10:57.21,Default,,0,0,0,,然后写一句指令
Dialogue: 0,0:10:57.23,0:10:58.75,Default,,0,0,0,,用来跳转到下一条指令
Dialogue: 0,0:11:02.20,0:11:04.56,Default,,0,0,0,,就是它结束之后要去的地方
Dialogue: 0,0:11:04.95,0:11:06.09,Default,,0,0,0,,你放入那个指令
Dialogue: 0,0:11:06.88,0:11:08.62,Default,,0,0,0,,这里你写下LABEL1
Dialogue: 0,0:11:12.12,0:11:13.80,Default,,0,0,0,,这里写A的代码
Dialogue: 0,0:11:19.47,0:11:25.85,Default,,0,0,0,,然后又是跳转到下一条指令
Dialogue: 0,0:11:31.42,0:11:32.88,Default,,0,0,0,,这就是处理条件分支的办法
Dialogue: 0,0:11:32.98,0:11:34.65,Default,,0,0,0,,你生成一小段这样的代码
Dialogue: 0,0:11:35.75,0:11:38.12,Default,,0,0,0,,除此之外
Dialogue: 0,0:11:38.95,0:11:41.55,Default,,0,0,0,,这个零阶编译器与求值器一模一样
Dialogue: 0,0:11:42.55,0:11:45.12,Default,,0,0,0,,它只是把指令存起来 而不执行它们
Dialogue: 0,0:11:46.55,0:11:47.60,Default,,0,0,0,,看起来很简单
Dialogue: 0,0:11:47.64,0:11:49.08,Default,,0,0,0,,但我们已经取得了某些收获
Dialogue: 0,0:11:50.12,0:11:52.62,Default,,0,0,0,,它会比求值器更有效率
Dialogue: 0,0:11:53.52,0:11:56.14,Default,,0,0,0,,因为 如果你观察求值器的运行
Dialogue: 0,0:11:56.35,0:12:01.05,Default,,0,0,0,,它并不只是进行寄存器操作
Dialogue: 0,0:12:01.27,0:12:03.50,Default,,0,0,0,,它还会决定执行哪个
Dialogue: 0,0:12:04.70,0:12:07.23,Default,,0,0,0,,它做的第一件事就是
Dialogue: 0,0:12:07.92,0:12:09.77,Default,,0,0,0,,以它为例 就是进行某些测试
Dialogue: 0,0:12:09.77,0:12:11.56,Default,,0,0,0,,确定它是一个应用
Dialogue: 0,0:12:13.57,0:12:15.05,Default,,0,0,0,,然后就跳转到
Dialogue: 0,0:12:15.39,0:12:16.62,Default,,0,0,0,,处理应用的地方去
Dialogue: 0,0:12:16.62,0:12:18.44,Default,,0,0,0,,换句话说 求值器做的事情是
Dialogue: 0,0:12:18.62,0:12:22.76,Default,,0,0,0,,分析代码需要进行的运算
Dialogue: 0,0:12:23.47,0:12:24.99,Default,,0,0,0,,同时并执行它们
Dialogue: 0,0:12:25.55,0:12:28.28,Default,,0,0,0,,当你运行求值器一百万次
Dialogue: 0,0:12:28.28,0:12:30.30,Default,,0,0,0,,这个分析过程就进行一百万次
Dialogue: 0,0:12:30.85,0:12:32.58,Default,,0,0,0,,而在编译器中 它只会进行一次
Dialogue: 0,0:12:32.58,0:12:34.81,Default,,0,0,0,,之后就只有寄存器操作了
Dialogue: 0,0:12:39.20,0:12:41.68,Default,,0,0,0,,这就是零阶编译器了
Dialogue: 0,0:12:41.80,0:12:44.04,Default,,0,0,0,,但它是个拙劣的编译器
Dialogue: 0,0:12:44.45,0:12:45.28,Default,,0,0,0,,它挺蠢的
Dialogue: 0,0:12:46.90,0:12:48.41,Default,,0,0,0,,让我们回过头来
Dialogue: 0,0:12:49.88,0:12:50.97,Default,,0,0,0,,看看这张投影
Dialogue: 0,0:12:52.02,0:12:55.29,Default,,0,0,0,,看看这个东西做的一些操作
Dialogue: 0,0:12:55.85,0:12:56.88,Default,,0,0,0,,我们想看看
Dialogue: 0,0:12:59.72,0:13:02.28,Default,,0,0,0,,在解释(F  X)时的操作
Dialogue: 0,0:13:03.52,0:13:04.84,Default,,0,0,0,,这里就是它做了什么
Dialogue: 0,0:13:05.17,0:13:06.11,Default,,0,0,0,,举个例子 这里
Dialogue: 0,0:13:07.15,0:13:11.98,Default,,0,0,0,,它将(OPERATOR (FETCH EXP))赋值给EXP
Dialogue: 0,0:13:13.75,0:13:15.87,Default,,0,0,0,,其实没必要这样做
Dialogue: 0,0:13:16.22,0:13:17.47,Default,,0,0,0,,因为编译器知道
Dialogue: 0,0:13:17.66,0:13:21.84,Default,,0,0,0,,(OPERATOR (FETCH EXP))的值就是F
Dialogue: 0,0:13:23.35,0:13:25.56,Default,,0,0,0,,因此这个指令没理由存在
Dialogue: 0,0:13:25.70,0:13:28.88,Default,,0,0,0,,应该改为:要把F赋值给EXP
Dialogue: 0,0:13:29.45,0:13:31.08,Default,,0,0,0,,或者实际上 你完全不需要EXP
Dialogue: 0,0:13:31.87,0:13:33.56,Default,,0,0,0,,没有理由需要EXP
Dialogue: 0,0:13:33.56,0:13:35.16,Default,,0,0,0,,EXP是用来做什么的?
Dialogue: 0,0:13:35.18,0:13:36.33,Default,,0,0,0,,我们看这里
Dialogue: 0,0:13:40.77,0:13:42.20,Default,,0,0,0,,我们对VAL赋值
Dialogue: 0,0:13:43.05,0:13:47.34,Default,,0,0,0,,在环境里的EXP里寻找东西
Dialogue: 0,0:13:48.68,0:13:49.53,Default,,0,0,0,,因此 我们实际上是要
Dialogue: 0,0:13:49.55,0:13:51.54,Default,,0,0,0,,替换掉所有的EXP寄存器
Dialogue: 0,0:13:51.54,0:13:53.32,Default,,0,0,0,,把这个指令修改为
Dialogue: 0,0:13:53.34,0:13:54.16,Default,,0,0,0,,给VAL赋值
Dialogue: 0,0:13:54.45,0:13:56.06,Default,,0,0,0,,在环境中查找
Dialogue: 0,0:13:56.36,0:13:58.40,Default,,0,0,0,,符号F的值
Dialogue: 0,0:14:01.09,0:14:01.77,Default,,0,0,0,,类似地
Dialogue: 0,0:14:02.57,0:14:04.27,Default,,0,0,0,,回到这里 我们也完全不需要UNEV
Dialogue: 0,0:14:04.72,0:14:05.79,Default,,0,0,0,,因为我们知道
Dialogue: 0,0:14:06.22,0:14:09.16,Default,,0,0,0,,因为我们知道 (FETCH EXP)取出的运算对象
Dialogue: 0,0:14:09.16,0:14:10.62,Default,,0,0,0,,就是'(X)
Dialogue: 0,0:14:13.25,0:14:14.06,Default,,0,0,0,,从某种意义上来说
Dialogue: 0,0:14:16.17,0:14:19.39,Default,,0,0,0,,你完全不需要UNEV和EXP
Dialogue: 0,0:14:19.67,0:14:21.05,Default,,0,0,0,,看看它们实际上是什么
Dialogue: 0,0:14:21.08,0:14:25.30,Default,,0,0,0,,它们不是实际运行机器的寄存器
Dialogue: 0,0:14:25.30,0:14:26.40,Default,,0,0,0,,它们实际上是
Dialogue: 0,0:14:26.60,0:14:29.50,Default,,0,0,0,,为了模拟该机器的而设置的寄存器
Dialogue: 0,0:14:30.72,0:14:33.77,Default,,0,0,0,,所以它们保存一些表达式
Dialogue: 0,0:14:34.00,0:14:36.04,Default,,0,0,0,,以编译器的视角看来
Dialogue: 0,0:14:36.06,0:14:36.81,Default,,0,0,0,,它们就是常量
Dialogue: 0,0:14:36.95,0:14:38.48,Default,,0,0,0,,因此可以直接把它们放到代码中
Dialogue: 0,0:14:39.47,0:14:41.34,Default,,0,0,0,,你可以忘掉那些关于
Dialogue: 0,0:14:41.36,0:14:42.54,Default,,0,0,0,,EXP和UNEV的操作
Dialogue: 0,0:14:42.57,0:14:43.77,Default,,0,0,0,,只用那些常量
Dialogue: 0,0:14:44.02,0:14:48.00,Default,,0,0,0,,与之相似 如果我们回顾这里
Dialogue: 0,0:14:48.00,0:14:51.32,Default,,0,0,0,,有像(ASSIGN CONTINUE EVAL-ARGS)之类的语句
Dialogue: 0,0:14:53.75,0:14:55.39,Default,,0,0,0,,现在 它和任何东西都没有关系
Dialogue: 0,0:14:55.62,0:14:57.76,Default,,0,0,0,,它只是求值器
Dialogue: 0,0:14:58.08,0:15:00.17,Default,,0,0,0,,维护了下一步需要去哪
Dialogue: 0,0:15:02.70,0:15:05.96,Default,,0,0,0,,在某些应用中对参数进行求值
Dialogue: 0,0:15:06.82,0:15:08.65,Default,,0,0,0,,当然 这与编译器没关系
Dialogue: 0,0:15:08.65,0:15:13.88,Default,,0,0,0,,因为这个分析过程已经被编译器做完了
Dialogue: 0,0:15:15.05,0:15:16.83,Default,,0,0,0,,所以编译后的代码完全不需要它
Dialogue: 0,0:15:17.70,0:15:19.32,Default,,0,0,0,,因此许多向CONTINUE寄存器
Dialogue: 0,0:15:19.32,0:15:21.30,Default,,0,0,0,,赋值的操作都是无用的
Dialogue: 0,0:15:21.30,0:15:24.62,Default,,0,0,0,,运行着的机器留着它们
Dialogue: 0,0:15:24.64,0:15:25.77,Default,,0,0,0,,是为了跟踪它的状态
Dialogue: 0,0:15:26.07,0:15:28.72,Default,,0,0,0,,是为了知道求值器的下一步分析
Dialogue: 0,0:15:28.72,0:15:30.03,Default,,0,0,0,,而它们是完全无关的
Dialogue: 0,0:15:30.06,0:15:31.23,Default,,0,0,0,,因此我们可以去掉它们
Dialogue: 0,0:15:43.90,0:15:45.98,Default,,0,0,0,,那么 如果我们简单地
Dialogue: 0,0:15:46.16,0:15:47.75,Default,,0,0,0,,进行这类优化
Dialogue: 0,0:15:47.75,0:15:51.64,Default,,0,0,0,,不再考虑EXP和UNEV
Dialogue: 0,0:15:51.75,0:15:56.22,Default,,0,0,0,,去掉这些无关的寄存器赋值
Dialogue: 0,0:15:57.25,0:15:59.96,Default,,0,0,0,,我们就可以找到这些代码
Dialogue: 0,0:16:01.48,0:16:06.20,Default,,0,0,0,,也就是求值器会执行的这19条指令
Dialogue: 0,0:16:06.91,0:16:08.12,Default,,0,0,0,,给替换掉
Dialogue: 0,0:16:08.36,0:16:10.33,Default,,0,0,0,,请看幻灯片
Dialogue: 0,0:16:12.27,0:16:15.34,Default,,0,0,0,,我们去掉了大概一半
Dialogue: 0,0:16:18.28,0:16:20.75,Default,,0,0,0,,同样 这就是某种过滤
Dialogue: 0,0:16:21.07,0:16:24.46,Default,,0,0,0,,把无关的东西去掉
Dialogue: 0,0:16:25.17,0:16:26.22,Default,,0,0,0,,你们看 比如说
Dialogue: 0,0:16:27.47,0:16:29.66,Default,,0,0,0,,这里 求值器说
Dialogue: 0,0:16:29.68,0:16:32.43,Default,,0,0,0,,(ASSIGN VAL (LOOKUP 'F (FETCH ENV)))
Dialogue: 0,0:16:32.46,0:16:34.22,Default,,0,0,0,,这里 我们放入了一个常量F
Dialogue: 0,0:16:35.44,0:16:37.02,Default,,0,0,0,,这里又放了一个常量X
Dialogue: 0,0:16:40.02,0:16:42.41,Default,,0,0,0,,因此 这个编译器又稍微好一点
Dialogue: 0,0:16:43.79,0:16:46.76,Default,,0,0,0,,但它还是比较蠢
Dialogue: 0,0:16:47.95,0:16:49.58,Default,,0,0,0,,它仍会做很多蠢事
Dialogue: 0,0:16:50.45,0:16:52.52,Default,,0,0,0,,我们再看幻灯片
Dialogue: 0,0:16:52.88,0:16:53.93,Default,,0,0,0,,看最开头的地方
Dialogue: 0,0:16:56.34,0:16:58.17,Default,,0,0,0,,我们调用(SAVE ENV)保存环境
Dialogue: 0,0:16:59.35,0:17:01.72,Default,,0,0,0,,然后给VAL寄存器赋某个值
Dialogue: 0,0:17:01.80,0:17:03.35,Default,,0,0,0,,然后恢复环境
Dialogue: 0,0:17:03.35,0:17:04.41,Default,,0,0,0,,它是从哪来的
Dialogue: 0,0:17:04.91,0:17:07.10,Default,,0,0,0,,它来自求值器的这个地方
Dialogue: 0,0:17:07.15,0:17:10.28,Default,,0,0,0,,哦 我在正在对一个应用求值
Dialogue: 0,0:17:11.10,0:17:14.68,Default,,0,0,0,,因此我要递归调用EVAL-DISPATCH
Dialogue: 0,0:17:15.87,0:17:17.98,Default,,0,0,0,,我最好把接下来要用到的东西
Dialogue: 0,0:17:17.98,0:17:19.08,Default,,0,0,0,,保存到环境中
Dialogue: 0,0:17:19.77,0:17:22.86,Default,,0,0,0,,这就是递归调用EVAL-DISPATCH的结果
Dialogue: 0,0:17:23.47,0:17:25.77,Default,,0,0,0,,刚才那个例子就是对符号F求值的结果
Dialogue: 0,0:17:26.50,0:17:28.27,Default,,0,0,0,,从EVAL-DISPATCH中返回
Dialogue: 0,0:17:28.28,0:17:29.66,Default,,0,0,0,,将环境恢复
Dialogue: 0,0:17:31.25,0:17:32.28,Default,,0,0,0,,但是实际上
Dialogue: 0,0:17:32.59,0:17:35.88,Default,,0,0,0,,这个求值过程中 所进行的操作
Dialogue: 0,0:17:35.92,0:17:37.71,Default,,0,0,0,,完全不会影响环境
Dialogue: 0,0:17:38.67,0:17:40.80,Default,,0,0,0,,所以这里没必要先保存环境
Dialogue: 0,0:17:40.84,0:17:42.22,Default,,0,0,0,,再恢复环境
Dialogue: 0,0:17:45.67,0:17:46.62,Default,,0,0,0,,与之类似
Dialogue: 0,0:17:49.79,0:17:51.39,Default,,0,0,0,,这里 我们保存了参数表
Dialogue: 0,0:17:53.07,0:17:55.80,Default,,0,0,0,,那是一个求值参数的循环
Dialogue: 0,0:17:55.82,0:17:56.86,Default,,0,0,0,,先保存参数表
Dialogue: 0,0:17:57.20,0:17:58.03,Default,,0,0,0,,然后在这里恢复
Dialogue: 0,0:17:58.08,0:18:00.51,Default,,0,0,0,,但事实上最后
Dialogue: 0,0:18:00.80,0:18:02.28,Default,,0,0,0,,并没有变更参数表
Dialogue: 0,0:18:02.84,0:18:04.17,Default,,0,0,0,,所以不需要保存它
Dialogue: 0,0:18:08.65,0:18:12.88,Default,,0,0,0,,换种方式来说
Dialogue: 0,0:18:13.77,0:18:14.80,Default,,0,0,0,,怎么说呢
Dialogue: 0,0:18:16.43,0:18:19.13,Default,,0,0,0,,求值器需要最大限度地保持悲观
Dialogue: 0,0:18:19.87,0:18:21.07,Default,,0,0,0,,因为 从它的视角来看
Dialogue: 0,0:18:21.08,0:18:23.06,Default,,0,0,0,,只知道接下来是要对某些东西进行求值
Dialogue: 0,0:18:23.24,0:18:24.97,Default,,0,0,0,,所以最好把稍后要用的都存下来
Dialogue: 0,0:18:26.12,0:18:27.79,Default,,0,0,0,,一旦你完成了分析
Dialogue: 0,0:18:27.82,0:18:29.68,Default,,0,0,0,,从编译器的角度就会考虑
Dialogue: 0,0:18:29.72,0:18:31.47,Default,,0,0,0,,哪些是我真正需要存下来的?
Dialogue: 0,0:18:32.12,0:18:33.31,Default,,0,0,0,,我们需要去 --
Dialogue: 0,0:18:33.42,0:18:37.30,Default,,0,0,0,,它不需要像求值器一样小心翼翼
Dialogue: 0,0:18:37.30,0:18:38.80,Default,,0,0,0,,因为它知道 实际需要什么
Dialogue: 0,0:18:39.69,0:18:41.16,Default,,0,0,0,,无论如何 如果我们完成了优化
Dialogue: 0,0:18:42.50,0:18:45.71,Default,,0,0,0,,消除掉所有多余的保存和恢复
Dialogue: 0,0:18:46.40,0:18:49.05,Default,,0,0,0,,那么我们可以得到这样的结果
Dialogue: 0,0:18:49.90,0:18:51.53,Default,,0,0,0,,我们可以发现
Dialogue: 0,0:18:51.64,0:18:53.71,Default,,0,0,0,,只有三条指令是必须的
Dialogue: 0,0:18:54.07,0:18:55.72,Default,,0,0,0,,从刚才的11条指令优化成这样
Dialogue: 0,0:18:55.97,0:18:58.81,Default,,0,0,0,,或是从原始的20条指令优化而来
Dialogue: 0,0:18:59.87,0:19:00.92,Default,,0,0,0,,这告诉我们
Dialogue: 0,0:19:01.12,0:19:03.18,Default,,0,0,0,,对于这些寄存器操作
Dialogue: 0,0:19:03.27,0:19:04.94,Default,,0,0,0,,哪些是必需的?
Dialogue: 0,0:19:09.42,0:19:11.74,Default,,0,0,0,,让我换个方式来总结一下
Dialogue: 0,0:19:11.74,0:19:13.48,Default,,0,0,0,,我先给你们看一张图
Dialogue: 0,0:19:16.00,0:19:17.52,Default,,0,0,0,,这张图片
Dialogue: 0,0:19:18.77,0:19:20.81,Default,,0,0,0,,展示了所有的保存和恢复
Dialogue: 0,0:19:23.50,0:19:25.23,Default,,0,0,0,,这里是表达式(F X)
Dialogue: 0,0:19:25.32,0:19:27.87,Default,,0,0,0,,在下面这里
Dialogue: 0,0:19:28.75,0:19:31.80,Default,,0,0,0,,是对求值器中各种地方的跟踪
Dialogue: 0,0:19:34.97,0:19:38.04,Default,,0,0,0,,在求值发生时会使用这些地方
Dialogue: 0,0:19:38.04,0:19:40.01,Default,,0,0,0,,在这里 你可以看到箭头
Dialogue: 0,0:19:40.22,0:19:42.08,Default,,0,0,0,,下箭头代表寄存器的保存
Dialogue: 0,0:19:42.40,0:19:44.84,Default,,0,0,0,,所以最先保存的是ENV寄存器
Dialogue: 0,0:19:46.82,0:19:48.68,Default,,0,0,0,,然后 在这里恢复ENV
Dialogue: 0,0:19:52.38,0:19:54.54,Default,,0,0,0,,这些都是成对的栈操作
Dialogue: 0,0:19:56.12,0:19:57.56,Default,,0,0,0,,如果你更进一步
Dialogue: 0,0:19:58.12,0:20:00.78,Default,,0,0,0,,我们记得
Dialogue: 0,0:20:00.89,0:20:03.02,Default,,0,0,0,,UNEV是个完全没用的寄存器
Dialogue: 0,0:20:07.80,0:20:09.78,Default,,0,0,0,,如果我们用固定结构的代码
Dialogue: 0,0:20:09.78,0:20:12.52,Default,,0,0,0,,就不需要保存UNEV 因为完全用不上
Dialogue: 0,0:20:16.20,0:20:19.15,Default,,0,0,0,,然后 根据我们约定的
Dialogue: 0,0:20:19.16,0:20:21.88,Default,,0,0,0,,应用过程的准则
Dialogue: 0,0:20:21.88,0:20:23.85,Default,,0,0,0,,我们会选择是否保存CONTINUE
Dialogue: 0,0:20:27.40,0:20:28.74,Default,,0,0,0,,这就是我们做的第一件事
Dialogue: 0,0:20:28.74,0:20:30.51,Default,,0,0,0,,然后我们可以看看
Dialogue: 0,0:20:31.71,0:20:32.70,Default,,0,0,0,,实际需要些什么
Dialogue: 0,0:20:33.07,0:20:35.56,Default,,0,0,0,,其实在求值F的过程中
Dialogue: 0,0:20:36.04,0:20:37.82,Default,,0,0,0,,我们不需要保存ENV
Dialogue: 0,0:20:38.08,0:20:39.92,Default,,0,0,0,,因为它不会被破坏
Dialogue: 0,0:20:39.92,0:20:41.31,Default,,0,0,0,,因此 如果我们利用这点
Dialogue: 0,0:20:44.12,0:20:47.56,Default,,0,0,0,,这里对F的求值
Dialogue: 0,0:20:48.57,0:20:50.44,Default,,0,0,0,,完全不需要担心
Dialogue: 0,0:20:51.61,0:20:52.60,Default,,0,0,0,,会破坏ENV
Dialogue: 0,0:20:52.60,0:20:54.94,Default,,0,0,0,,类似地 这里对X的求值
Dialogue: 0,0:20:57.17,0:20:58.89,Default,,0,0,0,,当求值器进行求值时 它会说
Dialogue: 0,0:20:58.91,0:21:01.64,Default,,0,0,0,,我最好保存好与之有关的FUN寄存器
Dialogue: 0,0:21:02.07,0:21:03.22,Default,,0,0,0,,因为后面也许会用得着
Dialogue: 0,0:21:03.28,0:21:04.89,Default,,0,0,0,,我最好也保存参数表
Dialogue: 0,0:21:06.90,0:21:09.05,Default,,0,0,0,,然而 在这如果是编译器的话
Dialogue: 0,0:21:09.05,0:21:10.38,Default,,0,0,0,,实际需要哪些寄存器
Dialogue: 0,0:21:10.52,0:21:11.84,Default,,0,0,0,,从而进行相关的保存与恢复
Dialogue: 0,0:21:12.70,0:21:16.09,Default,,0,0,0,,事实上 这里求值器做的所有栈操作
Dialogue: 0,0:21:16.32,0:21:19.58,Default,,0,0,0,,都证明是过于悲观而不必要
Dialogue: 0,0:21:19.62,0:21:21.45,Default,,0,0,0,,而编译器在这里是知道这一点的
Dialogue: 0,0:21:27.35,0:21:28.48,Default,,0,0,0,,这是最基础的想法
Dialogue: 0,0:21:29.80,0:21:31.00,Default,,0,0,0,,我们把求值器
Dialogue: 0,0:21:31.00,0:21:33.24,Default,,0,0,0,,剔除那些不需要的东西
Dialogue: 0,0:21:33.24,0:21:35.24,Default,,0,0,0,,去除那些对于编译器完全无用的东西
Dialogue: 0,0:21:35.24,0:21:36.19,Default,,0,0,0,,只保留求值的部分
Dialogue: 0,0:21:37.40,0:21:40.40,Default,,0,0,0,,然后你可以看到哪些栈操作是不必要的
Dialogue: 0,0:21:40.82,0:21:43.76,Default,,0,0,0,,这就是书中所描述的编译器
Dialogue: 0,0:21:43.85,0:21:45.04,Default,,0,0,0,,的基本结构
Dialogue: 0,0:21:45.04,0:21:47.00,Default,,0,0,0,,我给你们展示一下这
Dialogue: 0,0:21:47.76,0:21:49.68,Default,,0,0,0,,这个简单的例子
Dialogue: 0,0:21:51.20,0:21:53.26,Default,,0,0,0,,为了说清楚 多余的东西是怎样保存的
Dialogue: 0,0:21:53.29,0:21:56.06,Default,,0,0,0,,我们来看一个稍复杂的表达式
Dialogue: 0,0:21:58.15,0:22:01.93,Default,,0,0,0,,(F (G X) 1)
Dialogue: 0,0:22:03.87,0:22:05.52,Default,,0,0,0,,我们不会讲解所有的代码
Dialogue: 0,0:22:06.40,0:22:08.56,Default,,0,0,0,,因为代码有点多
Dialogue: 0,0:22:09.72,0:22:12.35,Default,,0,0,0,,我认为在求值器在处理它时
Dialogue: 0,0:22:12.35,0:22:14.67,Default,,0,0,0,,大概会产生
Dialogue: 0,0:22:14.70,0:22:16.25,Default,,0,0,0,,16对保存-恢复操作
Dialogue: 0,0:22:17.00,0:22:18.57,Default,,0,0,0,,这有一张图表
Dialogue: 0,0:22:20.57,0:22:21.95,Default,,0,0,0,,演示了其中的过程
Dialogue: 0,0:22:22.97,0:22:23.90,Default,,0,0,0,,你从这里开始--
Dialogue: 0,0:22:24.25,0:22:26.62,Default,,0,0,0,,求值器说:“我要求值一个应用”
Dialogue: 0,0:22:26.90,0:22:29.13,Default,,0,0,0,,在这里保存ENV 又在这里恢复
Dialogue: 0,0:22:30.65,0:22:34.44,Default,,0,0,0,,然后处理第一个运算对象
Dialogue: 0,0:22:36.81,0:22:39.28,Default,,0,0,0,,这是求值器的递归调用
Dialogue: 0,0:22:39.28,0:22:40.89,Default,,0,0,0,,求值器发现 这是一个应用
Dialogue: 0,0:22:40.91,0:22:42.10,Default,,0,0,0,,又会保存环境
Dialogue: 0,0:22:42.10,0:22:44.97,Default,,0,0,0,,求值组合式的运算符 然后在这里恢复环境
Dialogue: 0,0:22:45.80,0:22:48.92,Default,,0,0,0,,这个恢复匹配的是这个保存操作
Dialogue: 0,0:22:49.77,0:22:50.78,Default,,0,0,0,,以此类推
Dialogue: 0,0:22:51.65,0:22:52.51,Default,,0,0,0,,这里的UNEV
Dialogue: 0,0:22:52.52,0:22:54.62,Default,,0,0,0,,完全没有必要存在
Dialogue: 0,0:22:54.97,0:22:56.60,Default,,0,0,0,,CONTINUE寄存器不断地被保存-恢复
Dialogue: 0,0:22:57.42,0:23:00.41,Default,,0,0,0,,而FUN寄存器则是在
Dialogue: 0,0:23:00.78,0:23:04.36,Default,,0,0,0,,处理运算对象期间被保存
Dialogue: 0,0:23:05.10,0:23:06.52,Default,,0,0,0,,这类的事情一直在发生
Dialogue: 0,0:23:06.78,0:23:09.39,Default,,0,0,0,,但如果你问 跟求值器相比
Dialogue: 0,0:23:09.87,0:23:11.66,Default,,0,0,0,,编译器究竟要做什么?
Dialogue: 0,0:23:12.27,0:23:13.55,Default,,0,0,0,,你会去掉一大堆东西
Dialogue: 0,0:23:14.30,0:23:16.64,Default,,0,0,0,,在这个的基础上 如果你说
Dialogue: 0,0:23:19.40,0:23:22.54,Default,,0,0,0,,对F的求值不会修改ENV寄存器
Dialogue: 0,0:23:23.82,0:23:26.51,Default,,0,0,0,,或者对符号X的查找
Dialogue: 0,0:23:29.28,0:23:32.09,Default,,0,0,0,,不需要特别保护FUN寄存器
Dialogue: 0,0:23:34.30,0:23:37.60,Default,,0,0,0,,就得到了只有几对的保存-恢复操作
Dialogue: 0,0:23:40.25,0:23:42.27,Default,,0,0,0,,然而 你还可以再优化一下
Dialogue: 0,0:23:42.27,0:23:44.33,Default,,0,0,0,,看看这里的ENV寄存器发生了什么
Dialogue: 0,0:23:45.21,0:23:47.39,Default,,0,0,0,,我们观察ENV寄存器的操作 发现
Dialogue: 0,0:23:51.00,0:23:52.25,Default,,0,0,0,,这是一个组合式
Dialogue: 0,0:23:54.33,0:23:55.69,Default,,0,0,0,,而这个求值器
Dialogue: 0,0:23:55.78,0:23:57.27,Default,,0,0,0,,对G一无所知
Dialogue: 0,0:23:58.57,0:24:00.73,Default,,0,0,0,,所以在这 它说
Dialogue: 0,0:24:01.29,0:24:03.45,Default,,0,0,0,,我最好保存ENV寄存器
Dialogue: 0,0:24:03.96,0:24:05.42,Default,,0,0,0,,因为对G的求值
Dialogue: 0,0:24:05.42,0:24:07.42,Default,,0,0,0,,可能会修改ENV寄存器的值
Dialogue: 0,0:24:07.55,0:24:09.45,Default,,0,0,0,,而我稍后可能会需要它
Dialogue: 0,0:24:10.17,0:24:11.40,Default,,0,0,0,,在这个参数之后
Dialogue: 0,0:24:12.22,0:24:13.37,Default,,0,0,0,,在处理第二个参数的时候
Dialogue: 0,0:24:15.60,0:24:17.24,Default,,0,0,0,,这就是为什么它没被优化掉
Dialogue: 0,0:24:19.07,0:24:22.54,Default,,0,0,0,,因为编译器没有对G将要做的事情做任何假设
Dialogue: 0,0:24:22.54,0:24:23.60,Default,,0,0,0,,另一方面
Dialogue: 0,0:24:24.61,0:24:26.52,Default,,0,0,0,,如果你看看这里的第二个参数
Dialogue: 0,0:24:26.64,0:24:27.70,Default,,0,0,0,,它只是查找“1”这个常量
Dialogue: 0,0:24:27.70,0:24:29.60,Default,,0,0,0,,这不需要ENV寄存器
Dialogue: 0,0:24:30.77,0:24:32.04,Default,,0,0,0,,因此没必要保存它
Dialogue: 0,0:24:32.06,0:24:33.77,Default,,0,0,0,,事实上 你也可以把这个也去掉
Dialogue: 0,0:24:34.85,0:24:37.81,Default,,0,0,0,,这一堆寄存器操作
Dialogue: 0,0:24:37.98,0:24:40.08,Default,,0,0,0,,如果你像这样简单地推理的话
Dialogue: 0,0:24:40.55,0:24:43.05,Default,,0,0,0,,只会剩下两对保存-恢复操作
Dialogue: 0,0:24:45.10,0:24:46.97,Default,,0,0,0,,而这些 如果你知道关于G的某些信息的话
Dialogue: 0,0:24:47.52,0:24:49.08,Default,,0,0,0,,可以进一步优化
Dialogue: 0,0:24:56.27,0:24:57.85,Default,,0,0,0,,基本的理念是
Dialogue: 0,0:24:57.95,0:24:59.98,Default,,0,0,0,,编译器之所以更好
Dialogue: 0,0:24:59.98,0:25:02.56,Default,,0,0,0,,是因为解释器对于将要处理的东西一无所知
Dialogue: 0,0:25:03.25,0:25:05.04,Default,,0,0,0,,它不得不以最悲观的方式保存东西
Dialogue: 0,0:25:05.05,0:25:06.70,Default,,0,0,0,,来保护它自己
Dialogue: 0,0:25:07.90,0:25:08.76,Default,,0,0,0,,而编译器
Dialogue: 0,0:25:09.48,0:25:12.38,Default,,0,0,0,,只需要保存实际需要的东西
Dialogue: 0,0:25:13.37,0:25:15.20,Default,,0,0,0,,某个东西是否需要保存
Dialogue: 0,0:25:15.24,0:25:17.37,Default,,0,0,0,,有两种原因
Dialogue: 0,0:25:17.82,0:25:18.70,Default,,0,0,0,,一种是
Dialogue: 0,0:25:18.70,0:25:19.82,Default,,0,0,0,,你保护的东西
Dialogue: 0,0:25:19.95,0:25:21.44,Default,,0,0,0,,不会修改寄存器
Dialogue: 0,0:25:22.08,0:25:23.58,Default,,0,0,0,,例如 变量查找
Dialogue: 0,0:25:24.12,0:25:25.20,Default,,0,0,0,,另一种原因是
Dialogue: 0,0:25:25.32,0:25:27.10,Default,,0,0,0,,你所保存的东西
Dialogue: 0,0:25:28.28,0:25:29.92,Default,,0,0,0,,最后并不会被用到
Dialogue: 0,0:25:30.81,0:25:34.27,Default,,0,0,0,,因此 编译器正是利用了
Dialogue: 0,0:25:34.30,0:25:35.88,Default,,0,0,0,,这两条基本原则
Dialogue: 0,0:25:36.27,0:25:37.76,Default,,0,0,0,,来让代码变得更高效的
Dialogue: 0,0:25:44.27,0:25:45.32,Default,,0,0,0,,有什么问题吗?
Dialogue: 0,0:25:51.20,0:25:53.10,Default,,0,0,0,,学生: 你一直在说UNEV寄存器
Dialogue: 0,0:25:53.13,0:25:56.40,Default,,0,0,0,,UNEV寄存器完全不会被用到
Dialogue: 0,0:25:56.41,0:25:58.68,Default,,0,0,0,,是否意味着 机器只需要6个寄存器足矣?
Dialogue: 0,0:25:58.70,0:26:00.08,Default,,0,0,0,,或者是说 在这个特定的例子里
Dialogue: 0,0:26:00.11,0:26:01.18,Default,,0,0,0,,它没有被用到?
Dialogue: 0,0:26:01.72,0:26:02.81,Default,,0,0,0,,教授: 对于编译器
Dialogue: 0,0:26:04.31,0:26:07.42,Default,,0,0,0,,你可以生成6个或5个寄存器的代码
Dialogue: 0,0:26:07.56,0:26:09.02,Default,,0,0,0,,因为EXP寄存器也没有用到
Dialogue: 0,0:26:09.40,0:26:14.57,Default,,0,0,0,,是的 你可以把EXP和UNEV都去掉
Dialogue: 0,0:26:14.57,0:26:16.87,Default,,0,0,0,,因为这些是求值器的数据结构
Dialogue: 0,0:26:17.36,0:26:19.36,Default,,0,0,0,,以编译器的视角来看
Dialogue: 0,0:26:19.39,0:26:20.87,Default,,0,0,0,,这些东西都是常量
Dialogue: 0,0:26:21.65,0:26:22.44,Default,,0,0,0,,关键在于
Dialogue: 0,0:26:22.48,0:26:24.59,Default,,0,0,0,,这个特定编译器是被构造出来的
Dialogue: 0,0:26:24.79,0:26:27.92,Default,,0,0,0,,因此被解释的代码和被编译的代码可以共存
Dialogue: 0,0:26:29.32,0:26:30.72,Default,,0,0,0,,可以这样看待它
Dialogue: 0,0:26:30.97,0:26:32.29,Default,,0,0,0,,你构建了一个芯片
Dialogue: 0,0:26:34.30,0:26:35.50,Default,,0,0,0,,它就是求值器
Dialogue: 0,0:26:35.88,0:26:37.28,Default,,0,0,0,,而编译器可以做的就是
Dialogue: 0,0:26:37.31,0:26:39.02,Default,,0,0,0,,为这个芯片生成代码
Dialogue: 0,0:26:40.40,0:26:41.90,Default,,0,0,0,,只是它不会用到两个寄存器而已
Dialogue: 0,0:26:51.52,0:26:52.47,Default,,0,0,0,,好 休息一会
Dialogue: 0,0:26:53.55,0:27:07.18,Default,,0,0,0,,[音乐]
Dialogue: 0,0:27:07.37,0:27:11.42,Declare,,0,0,0,,{\an2\fad(500,500)}《计算机程序的构造和解释》
Dialogue: 0,0:27:14.57,0:27:18.12,Declare,,0,0,0,,{\an2\fad(500,500)}讲师: 哈罗德·艾伯森教授 及 格兰德·杰·萨斯曼教授
Dialogue: 0,0:27:18.17,0:27:22.08,Declare,,0,0,0,,{\an2\fad(500,500)}《计算机程序的构造和解释》
Dialogue: 0,0:27:22.22,0:27:26.48,Declare,,0,0,0,,{\an2\fad(500,500)}编译
Dialogue: 0,0:27:29.21,0:27:32.43,Default,,0,0,0,,我们刚才研究了编译器应该要做什么
Dialogue: 0,0:27:32.78,0:27:36.04,Default,,0,0,0,,现在我们来简略地看看
Dialogue: 0,0:27:36.15,0:27:37.47,Default,,0,0,0,,这些目标如何达成
Dialogue: 0,0:27:38.26,0:27:39.58,Default,,0,0,0,,而我不会给出细节
Dialogue: 0,0:27:39.60,0:27:42.17,Default,,0,0,0,,在书中有一大堆代码
Dialogue: 0,0:27:42.22,0:27:43.42,Default,,0,0,0,,展示了所有细节
Dialogue: 0,0:27:43.45,0:27:45.31,Default,,0,0,0,,我要做的 是给你们展示
Dialogue: 0,0:27:45.96,0:27:47.26,Default,,0,0,0,,其中的关键思想
Dialogue: 0,0:27:49.49,0:27:51.36,Default,,0,0,0,,换个时间再来关心细节
Dialogue: 0,0:27:51.51,0:27:55.30,Default,,0,0,0,,设想我们正在编译一条表达式
Dialogue: 0,0:27:55.30,0:27:57.01,Default,,0,0,0,,这里有一些运算符
Dialogue: 0,0:27:57.48,0:27:58.56,Default,,0,0,0,,和两个参数
Dialogue: 0,0:28:03.56,0:28:04.24,Default,,0,0,0,,现在
Dialogue: 0,0:28:06.27,0:28:08.14,Default,,0,0,0,,这个编译器会生成什么代码?
Dialogue: 0,0:28:08.85,0:28:09.78,Default,,0,0,0,,首先
Dialogue: 0,0:28:09.83,0:28:11.20,Default,,0,0,0,,它会递归运行
Dialogue: 0,0:28:11.90,0:28:13.28,Default,,0,0,0,,编译运算符
Dialogue: 0,0:28:14.37,0:28:19.02,Default,,0,0,0,,它说 我要编译运算符
Dialogue: 0,0:28:21.16,0:28:24.54,Default,,0,0,0,,最后我需要让它们的结果
Dialogue: 0,0:28:24.84,0:28:27.95,Default,,0,0,0,,存放在FUN寄存器中
Dialogue: 0,0:28:28.42,0:28:29.60,Default,,0,0,0,,所以我编译一些指令
Dialogue: 0,0:28:29.64,0:28:31.56,Default,,0,0,0,,它们会编译运算符
Dialogue: 0,0:28:31.69,0:28:38.62,Default,,0,0,0,,最后把结果放在FUN寄存器中
Dialogue: 0,0:28:45.51,0:28:46.94,Default,,0,0,0,,接下来我要做的是
Dialogue: 0,0:28:47.71,0:28:49.68,Default,,0,0,0,,另一个代码片段则说
Dialogue: 0,0:28:49.68,0:28:55.17,Default,,0,0,0,,我要编译第一个参数
Dialogue: 0,0:28:55.17,0:28:56.80,Default,,0,0,0,,因此它递归调地用自己
Dialogue: 0,0:28:58.04,0:29:03.36,Default,,0,0,0,,而结果会被放在VAL中
Dialogue: 0,0:29:09.07,0:29:10.75,Default,,0,0,0,,接下来需要做的是
Dialogue: 0,0:29:10.75,0:29:12.26,Default,,0,0,0,,建立起参数表
Dialogue: 0,0:29:12.95,0:29:25.50,Default,,0,0,0,,(ASSIGN ARGL (CONS (FETCH --
Dialogue: 0,0:29:25.55,0:29:27.10,Default,,0,0,0,,它会生成这些代码
Dialogue: 0,0:29:27.50,0:29:32.51,Default,,0,0,0,,(FETCH VAL) '()))
Dialogue: 0,0:29:35.00,0:29:36.05,Default,,0,0,0,,然而
Dialogue: 0,0:29:37.99,0:29:40.61,Default,,0,0,0,,当它到这里时
Dialogue: 0,0:29:41.32,0:29:42.82,Default,,0,0,0,,它可能需要环境
Dialogue: 0,0:29:43.95,0:29:45.29,Default,,0,0,0,,它需要环境
Dialogue: 0,0:29:45.32,0:29:48.21,Default,,0,0,0,,这是求值第一个参数所需要的
Dialogue: 0,0:29:49.04,0:29:51.18,Default,,0,0,0,,因此 它需要保证
Dialogue: 0,0:29:51.92,0:29:53.76,Default,,0,0,0,,对运算对象的编译
Dialogue: 0,0:29:55.32,0:29:57.85,Default,,0,0,0,,或者说它需要保护FUN寄存器
Dialogue: 0,0:29:58.01,0:30:00.98,Default,,0,0,0,,来应对编译运算对象时发生的各种情况
Dialogue: 0,0:30:01.30,0:30:03.08,Default,,0,0,0,,因此它在这做了个标注说
Dialogue: 0,0:30:03.37,0:30:12.89,Default,,0,0,0,,这个片段需要保护ENV寄存器
Dialogue: 0,0:30:17.39,0:30:18.44,Default,,0,0,0,,与之类似 这里
Dialogue: 0,0:30:21.02,0:30:23.30,Default,,0,0,0,,在完成第一个运算对象的编译后
Dialogue: 0,0:30:23.57,0:30:24.67,Default,,0,0,0,,它会说 我最好--
Dialogue: 0,0:30:24.71,0:30:27.92,Default,,0,0,0,,我需要知道第二个运算对象的环境
Dialogue: 0,0:30:27.92,0:30:29.46,Default,,0,0,0,,所以它在这做了个标注
Dialogue: 0,0:30:29.71,0:30:35.96,Default,,0,0,0,,这里也需要保护ENV
Dialogue: 0,0:30:39.42,0:30:41.02,Default,,0,0,0,,现在它继续运行
Dialogue: 0,0:30:41.12,0:30:42.83,Default,,0,0,0,,下一段代码
Dialogue: 0,0:30:43.31,0:30:49.74,Default,,0,0,0,,是要编译第二个参数
Dialogue: 0,0:30:50.82,0:30:52.64,Default,,0,0,0,,它将会
Dialogue: 0,0:30:52.99,0:30:59.28,Default,,0,0,0,,把编译的结果按约定放入到VAL中
Dialogue: 0,0:31:03.86,0:31:06.70,Default,,0,0,0,,随后它会生成一条指令
Dialogue: 0,0:31:07.84,0:31:09.25,Default,,0,0,0,,从而建立起参数表
Dialogue: 0,0:31:09.55,0:31:15.28,Default,,0,0,0,,(ASSIGN ARGL
Dialogue: 0,0:31:20.22,0:31:28.94,Default,,0,0,0,,(CONS (FETCH VAL) (FETCH ARGL))
Dialogue: 0,0:31:33.97,0:31:34.64,Default,,0,0,0,,然而
Dialogue: 0,0:31:34.81,0:31:36.58,Default,,0,0,0,,为了取得旧的参数表
Dialogue: 0,0:31:37.15,0:31:40.99,Default,,0,0,0,,它最好保证这期间发生的任何事情
Dialogue: 0,0:31:41.30,0:31:42.69,Default,,0,0,0,,都不影响旧的参数表
Dialogue: 0,0:31:43.50,0:31:45.17,Default,,0,0,0,,因此它在这做了个标注说
Dialogue: 0,0:31:45.34,0:31:51.64,Default,,0,0,0,,哦 这里需要保护ARGL
Dialogue: 0,0:31:54.16,0:31:56.03,Default,,0,0,0,,现在参数表就建立好了
Dialogue: 0,0:31:58.01,0:32:02.86,Default,,0,0,0,,现在可以准备去APPLY-DISPATCH了
Dialogue: 0,0:32:07.02,0:32:10.80,Default,,0,0,0,,它生成了这条指令
Dialogue: 0,0:32:15.19,0:32:17.37,Default,,0,0,0,,因为现在参数都在ARGL中
Dialogue: 0,0:32:18.15,0:32:20.59,Default,,0,0,0,,运算符在FUN中
Dialogue: 0,0:32:20.59,0:32:22.89,Default,,0,0,0,,如果只是单纯地把运算符放到FUN寄存器中
Dialogue: 0,0:32:23.27,0:32:26.64,Default,,0,0,0,,就需要它保证这块代码
Dialogue: 0,0:32:27.09,0:32:29.27,Default,,0,0,0,,不会破坏FUN寄存器里的东西
Dialogue: 0,0:32:29.67,0:32:31.24,Default,,0,0,0,,所以它在这做了个标注说
Dialogue: 0,0:32:31.55,0:32:32.73,Default,,0,0,0,,这里的所有东西
Dialogue: 0,0:32:34.88,0:32:40.73,Default,,0,0,0,,最好能够在保护FUN寄存器的情况下完成
Dialogue: 0,0:32:43.71,0:32:46.15,Default,,0,0,0,,所以这就是--
Dialogue: 0,0:32:46.15,0:32:47.10,Default,,0,0,0,,基本上来说
Dialogue: 0,0:32:48.20,0:32:50.24,Default,,0,0,0,,编译器所做的就是
Dialogue: 0,0:32:50.54,0:32:52.46,Default,,0,0,0,,追加一大堆的代码
Dialogue: 0,0:32:53.50,0:32:58.83,Default,,0,0,0,,而这些代码之中都是一些基本运算
Dialogue: 0,0:32:58.86,0:33:00.12,Default,,0,0,0,,比如符号查找
Dialogue: 0,0:33:01.44,0:33:02.60,Default,,0,0,0,,条件分支的处理
Dialogue: 0,0:33:02.64,0:33:05.44,Default,,0,0,0,,都是一些琐碎的事情
Dialogue: 0,0:33:05.44,0:33:07.99,Default,,0,0,0,,然后按照这种准则将它们追加到一起
Dialogue: 0,0:33:08.78,0:33:10.79,Default,,0,0,0,,因此 组合的基本手段就是
Dialogue: 0,0:33:10.86,0:33:13.18,Default,,0,0,0,,将一段代码追加到另一段的后面
Dialogue: 0,0:33:21.55,0:33:22.86,Default,,0,0,0,,就是这里发生的事情
Dialogue: 0,0:33:25.58,0:33:27.24,Default,,0,0,0,,这有点取巧
Dialogue: 0,0:33:27.56,0:33:30.37,Default,,0,0,0,,向一段代码后面追加代码的思路是
Dialogue: 0,0:33:31.60,0:33:33.76,Default,,0,0,0,,小心保护寄存器
Dialogue: 0,0:33:35.63,0:33:37.93,Default,,0,0,0,,追加操作看起来像这样
Dialogue: 0,0:33:39.15,0:33:40.65,Default,,0,0,0,,它要做的是
Dialogue: 0,0:33:41.20,0:33:44.11,Default,,0,0,0,,代码的追加是这么来做的
Dialogue: 0,0:33:44.53,0:33:53.63,Default,,0,0,0,,如果SEQ1需要寄存器--
Dialogue: 0,0:33:53.66,0:33:54.72,Default,,0,0,0,,我应该改一下这个
Dialogue: 0,0:33:54.72,0:33:56.87,Default,,0,0,0,,在SEQ1后面追加SEQ2
Dialogue: 0,0:33:57.42,0:34:03.96,Default,,0,0,0,,并保护一些寄存器
Dialogue: 0,0:34:08.52,0:34:09.91,Default,,0,0,0,,这里改成AND
Dialogue: 0,0:34:11.36,0:34:13.03,Default,,0,0,0,,这样的话前后顺序就清楚了
Dialogue: 0,0:34:13.88,0:34:19.87,Default,,0,0,0,,如果SEQ2需要寄存器
Dialogue: 0,0:34:21.12,0:34:27.85,Default,,0,0,0,,而SEQ1又修改了寄存器
Dialogue: 0,0:34:33.68,0:34:36.30,Default,,0,0,0,,那么编译器生成的指令是
Dialogue: 0,0:34:36.97,0:34:41.34,Default,,0,0,0,,保存寄存器
Dialogue: 0,0:34:43.02,0:34:44.19,Default,,0,0,0,,这就是代码
Dialogue: 0,0:34:44.35,0:34:45.35,Default,,0,0,0,,生成这段代码
Dialogue: 0,0:34:45.35,0:34:46.28,Default,,0,0,0,,保存寄存器
Dialogue: 0,0:34:46.72,0:34:52.97,Default,,0,0,0,,然后写下递归编译SEQ1的结果
Dialogue: 0,0:34:53.30,0:34:54.84,Default,,0,0,0,,然后恢复寄存器
Dialogue: 0,0:35:00.52,0:35:03.92,Default,,0,0,0,,再写下递归编译
Dialogue: 0,0:35:04.46,0:35:05.47,Default,,0,0,0,,SEQ2的结果
Dialogue: 0,0:35:07.07,0:35:09.62,Default,,0,0,0,,这就是你需要做的
Dialogue: 0,0:35:09.62,0:35:11.82,Default,,0,0,0,,实际上SEQ2需要寄存器
Dialogue: 0,0:35:11.82,0:35:13.74,Default,,0,0,0,,而SEQ1改动了它
Dialogue: 0,0:35:15.12,0:35:17.07,Default,,0,0,0,,否则的话
Dialogue: 0,0:35:20.50,0:35:26.57,Default,,0,0,0,,得到的就是SEQ1后面跟着SEQ2
Dialogue: 0,0:35:28.17,0:35:30.30,Default,,0,0,0,,这就是把两个代码片段
Dialogue: 0,0:35:30.59,0:35:33.52,Default,,0,0,0,,连接到一起的基本操作
Dialogue: 0,0:35:33.93,0:35:35.93,Default,,0,0,0,,把这些指令组合成序列
Dialogue: 0,0:35:36.89,0:35:38.87,Default,,0,0,0,,我们可以发现 从这个角度看
Dialogue: 0,0:35:40.94,0:35:45.96,Default,,0,0,0,,解释器和编译器的区别
Dialogue: 0,0:35:46.82,0:35:49.34,Default,,0,0,0,,是编译器有保护寄存器的标注
Dialogue: 0,0:35:50.14,0:35:52.22,Default,,0,0,0,,上面记录着
Dialogue: 0,0:35:52.49,0:35:54.22,Default,,0,0,0,,是否需要生成保存-恢复代码
Dialogue: 0,0:35:55.19,0:35:57.24,Default,,0,0,0,,而解释器会以最悲观的方式处理
Dialogue: 0,0:35:57.28,0:35:58.90,Default,,0,0,0,,总是会进行保存-恢复
Dialogue: 0,0:36:00.76,0:36:01.93,Default,,0,0,0,,这就是关键的区别
Dialogue: 0,0:36:04.16,0:36:06.05,Default,,0,0,0,,为了实现这个
Dialogue: 0,0:36:06.65,0:36:09.40,Default,,0,0,0,,编译器需要一些理论
Dialogue: 0,0:36:09.56,0:36:11.96,Default,,0,0,0,,来确定代码序列会需要、又会修改哪些寄存器
Dialogue: 0,0:36:14.26,0:36:17.28,Default,,0,0,0,,所以你放入的小片段
Dialogue: 0,0:36:17.48,0:36:21.00,Default,,0,0,0,,例如这段基础代码
Dialogue: 0,0:36:22.74,0:36:24.59,Default,,0,0,0,,当你查找一个变量时
Dialogue: 0,0:36:24.92,0:36:26.04,Default,,0,0,0,,进行了哪些操作?
Dialogue: 0,0:36:26.89,0:36:29.02,Default,,0,0,0,,你又是做了些什么
Dialogue: 0,0:36:29.05,0:36:30.68,Default,,0,0,0,,来编译一个常量
Dialogue: 0,0:36:30.97,0:36:32.10,Default,,0,0,0,,或者应用一个函数
Dialogue: 0,0:36:32.97,0:36:34.48,Default,,0,0,0,,它们都会带有一些标注
Dialogue: 0,0:36:34.67,0:36:36.46,Default,,0,0,0,,说明了它们需要的和修改的寄存器
Dialogue: 0,0:36:38.78,0:36:41.50,Default,,0,0,0,,所以底层的数据结构
Dialogue: 0,0:36:42.66,0:36:44.33,Default,,0,0,0,,我会这样讲
Dialogue: 0,0:36:44.39,0:36:47.91,Default,,0,0,0,,传递给编译器的代码序列大概是这样
Dialogue: 0,0:36:48.07,0:36:51.42,Default,,0,0,0,,它里面有实际的指令序列
Dialogue: 0,0:36:55.67,0:36:56.81,Default,,0,0,0,,跟它一起的还有
Dialogue: 0,0:36:57.18,0:37:02.60,Default,,0,0,0,,一组被修改的寄存器
Dialogue: 0,0:37:10.54,0:37:12.60,Default,,0,0,0,,还有一组需要的寄存器
Dialogue: 0,0:37:20.00,0:37:22.46,Default,,0,0,0,,为了能够执行此操作
Dialogue: 0,0:37:23.00,0:37:26.41,Default,,0,0,0,,编译器必须要掌握这些信息
Dialogue: 0,0:37:29.30,0:37:31.08,Default,,0,0,0,,它们从哪来呢
Dialogue: 0,0:37:32.91,0:37:34.49,Default,,0,0,0,,它们来自于--你们可能也想到了
Dialogue: 0,0:37:34.51,0:37:35.53,Default,,0,0,0,,对于那些最基本的片段
Dialogue: 0,0:37:35.55,0:37:36.84,Default,,0,0,0,,我们会手工添加
Dialogue: 0,0:37:37.24,0:37:38.86,Default,,0,0,0,,然后 当我们组合两个序列时
Dialogue: 0,0:37:38.89,0:37:41.02,Default,,0,0,0,,我们会计算出这两个集合
Dialogue: 0,0:37:42.16,0:37:44.12,Default,,0,0,0,,举一个非常基本的例子
Dialogue: 0,0:37:48.43,0:37:51.40,Default,,0,0,0,,例如做一个寄存器赋值
Dialogue: 0,0:37:51.77,0:37:53.50,Default,,0,0,0,,因此 基本代码片段会说
Dialogue: 0,0:37:53.52,0:37:56.22,Default,,0,0,0,,噢 它是个代码片段
Dialogue: 0,0:37:56.22,0:38:03.17,Default,,0,0,0,,代码的指令部分是(ASSIGN R1 (FETCH R2))
Dialogue: 0,0:38:03.17,0:38:04.27,Default,,0,0,0,,这个例子就是这样的
Dialogue: 0,0:38:05.42,0:38:08.52,Default,,0,0,0,,一段指令序列大概就是这样
Dialogue: 0,0:38:08.77,0:38:10.53,Default,,0,0,0,,和它在一起的是
Dialogue: 0,0:38:10.64,0:38:15.76,Default,,0,0,0,,它需要记得修改了R1
Dialogue: 0,0:38:18.60,0:38:21.16,Default,,0,0,0,,然后它需要R2
Dialogue: 0,0:38:24.69,0:38:26.99,Default,,0,0,0,,因此当你开始构建编译器时
Dialogue: 0,0:38:27.10,0:38:29.35,Default,,0,0,0,,你放入这样的一个片段
Dialogue: 0,0:38:30.95,0:38:33.20,Default,,0,0,0,,当它组合两个序列时
Dialogue: 0,0:38:36.70,0:38:38.04,Default,,0,0,0,,我要组合
Dialogue: 0,0:38:38.92,0:38:41.58,Default,,0,0,0,,代码片段S1
Dialogue: 0,0:38:42.88,0:38:47.16,Default,,0,0,0,,修改了一组寄存器M1
Dialogue: 0,0:38:48.45,0:38:51.42,Default,,0,0,0,,并且需要一组寄存器N1
Dialogue: 0,0:38:54.85,0:38:59.48,Default,,0,0,0,,并且我要把它和序列S2组合到一起
Dialogue: 0,0:39:00.81,0:39:05.96,Default,,0,0,0,,后者修改了一组寄存器M2
Dialogue: 0,0:39:07.11,0:39:10.00,Default,,0,0,0,,并且需要一组寄存器N2
Dialogue: 0,0:39:12.44,0:39:14.83,Default,,0,0,0,,这样我们就能得出结果
Dialogue: 0,0:39:15.11,0:39:16.32,Default,,0,0,0,,新的代码片段是这样的
Dialogue: 0,0:39:17.18,0:39:21.82,Default,,0,0,0,,指令序列S1后面跟着S2
Dialogue: 0,0:39:24.09,0:39:26.45,Default,,0,0,0,,它要修改什么?
Dialogue: 0,0:39:27.80,0:39:29.18,Default,,0,0,0,,它要修改的是
Dialogue: 0,0:39:29.20,0:39:32.68,Default,,0,0,0,,被S1或者被S2修改的寄存器
Dialogue: 0,0:39:34.00,0:39:36.35,Default,,0,0,0,,N1和N2的并集
Dialogue: 0,0:39:37.68,0:39:39.64,Default,,0,0,0,,就是新的修改集
Dialogue: 0,0:39:40.46,0:39:41.79,Default,,0,0,0,,然后你问
Dialogue: 0,0:39:44.66,0:39:46.41,Default,,0,0,0,,又需要哪些寄存器?
Dialogue: 0,0:39:47.95,0:39:49.77,Default,,0,0,0,,需要这些寄存器的是
Dialogue: 0,0:39:49.93,0:39:51.85,Default,,0,0,0,,首先 一定是序列S1需要的
Dialogue: 0,0:39:52.91,0:39:54.49,Default,,0,0,0,,因此必然有N1
Dialogue: 0,0:39:55.19,0:39:58.28,Default,,0,0,0,,然后 并不是N2里面的所有元素
Dialogue: 0,0:39:58.32,0:39:59.61,Default,,0,0,0,,我们都需要
Dialogue: 0,0:39:59.75,0:40:03.49,Default,,0,0,0,,新的修改集需要N2中那些
Dialogue: 0,0:40:03.88,0:40:06.88,Default,,0,0,0,,没有被S1修改过的寄存器
Dialogue: 0,0:40:08.14,0:40:09.72,Default,,0,0,0,,所以 这个并集是N1并上
Dialogue: 0,0:40:11.66,0:40:13.40,Default,,0,0,0,,序列S2的需要集N2
Dialogue: 0,0:40:14.51,0:40:18.52,Default,,0,0,0,,减去序列S1的修改集M1
Dialogue: 0,0:40:19.31,0:40:20.88,Default,,0,0,0,,因为它关心的是如何设置它们
Dialogue: 0,0:40:23.95,0:40:26.26,Default,,0,0,0,,这就是编译器的基本结构
Dialogue: 0,0:40:26.70,0:40:29.82,Default,,0,0,0,,我们进行寄存器优化的方式是
Dialogue: 0,0:40:30.22,0:40:32.70,Default,,0,0,0,,一些策略来应对需要保护的东西
Dialogue: 0,0:40:34.10,0:40:35.63,Default,,0,0,0,,这取决于数据结构
Dialogue: 0,0:40:35.72,0:40:38.51,Default,,0,0,0,,这取决于将东西组合在一起的操作
Dialogue: 0,0:40:39.03,0:40:41.63,Default,,0,0,0,,想知道要保护哪些东西
Dialogue: 0,0:40:41.93,0:40:47.28,Default,,0,0,0,,就需要知道这段代码需要以及修改的寄存器
Dialogue: 0,0:40:48.75,0:40:51.26,Default,,0,0,0,,这就需要我们有一个数据结构
Dialogue: 0,0:40:51.42,0:40:55.43,Default,,0,0,0,,它不但要存放实际的指令序列
Dialogue: 0,0:40:55.60,0:40:57.33,Default,,0,0,0,,它修改了什么 又需要什么
Dialogue: 0,0:40:57.33,0:40:59.77,Default,,0,0,0,,这些信息来自于--最基本的情况是内置的
Dialogue: 0,0:40:59.79,0:41:01.36,Default,,0,0,0,,对于最基本的情况
Dialogue: 0,0:41:01.37,0:41:02.52,Default,,0,0,0,,我们可以容易地知道
Dialogue: 0,0:41:03.00,0:41:04.44,Default,,0,0,0,,需要哪些寄存器 又修改了哪些
Dialogue: 0,0:41:04.82,0:41:05.35,Default,,0,0,0,,另外
Dialogue: 0,0:41:05.44,0:41:08.60,Default,,0,0,0,,利用这个特定的方法构建复杂指令时
Dialogue: 0,0:41:09.28,0:41:11.89,Default,,0,0,0,,我们可以像这样生成新的修改集
Dialogue: 0,0:41:11.93,0:41:13.37,Default,,0,0,0,,以及新的需要集
Dialogue: 0,0:41:15.27,0:41:17.77,Default,,0,0,0,,这就是全部的内容 -- 我不该这么说
Dialogue: 0,0:41:17.77,0:41:19.34,Default,,0,0,0,,这就是书里面大概30页的细节
Dialogue: 0,0:41:19.74,0:41:21.87,Default,,0,0,0,,的核心内容了
Dialogue: 0,0:41:22.31,0:41:27.69,Default,,0,0,0,,但它是一个完全可用的初级编译器
Dialogue: 0,0:41:28.76,0:41:31.37,Default,,0,0,0,,让我给你展示一下它能做什么
Dialogue: 0,0:41:31.39,0:41:35.56,Default,,0,0,0,,假设我们从一个递归阶乘开始
Dialogue: 0,0:41:36.20,0:41:38.60,Default,,0,0,0,,这些幻灯片的字太小不适合阅读
Dialogue: 0,0:41:38.60,0:41:39.79,Default,,0,0,0,,我只想快速翻一下代码
Dialogue: 0,0:41:39.79,0:41:41.28,Default,,0,0,0,,让你们看看它有多少代码
Dialogue: 0,0:41:42.25,0:41:43.29,Default,,0,0,0,,代码从这开始--
Dialogue: 0,0:41:44.32,0:41:45.68,Default,,0,0,0,,这是代码的第一部分
Dialogue: 0,0:41:45.95,0:41:47.68,Default,,0,0,0,,这里编译了一个过程入口
Dialogue: 0,0:41:47.69,0:41:48.73,Default,,0,0,0,,并进行了一些赋值操作
Dialogue: 0,0:41:48.75,0:41:51.48,Default,,0,0,0,,这基本上对应了解释器中
Dialogue: 0,0:41:52.65,0:41:53.90,Default,,0,0,0,,进行判断之前的部分
Dialogue: 0,0:41:54.31,0:41:56.59,Default,,0,0,0,,并判断谓词是否成立
Dialogue: 0,0:41:56.97,0:41:57.85,Default,,0,0,0,,第二部分是
Dialogue: 0,0:41:58.46,0:42:03.73,Default,,0,0,0,,递归调用N-1的阶乘的结果
Dialogue: 0,0:42:04.12,0:42:05.05,Default,,0,0,0,,最后一部分是
Dialogue: 0,0:42:06.07,0:42:07.48,Default,,0,0,0,,从那里返回
Dialogue: 0,0:42:07.87,0:42:09.90,Default,,0,0,0,,并处理递归的基本情况
Dialogue: 0,0:42:09.90,0:42:13.16,Default,,0,0,0,,这就是编译阶乘会生成的代码量
Dialogue: 0,0:42:13.72,0:42:17.69,Default,,0,0,0,,当然 我们可以把这个编译器做得更好
Dialogue: 0,0:42:18.67,0:42:21.24,Default,,0,0,0,,优化它的主要方式是
Dialogue: 0,0:42:21.24,0:42:24.00,Default,,0,0,0,,当你调用一个过程时
Dialogue: 0,0:42:24.35,0:42:26.27,Default,,0,0,0,,允许编译器做任何假设
Dialogue: 0,0:42:26.97,0:42:28.28,Default,,0,0,0,,举例来说
Dialogue: 0,0:42:28.30,0:42:32.32,Default,,0,0,0,,这个编译器甚至不知道
Dialogue: 0,0:42:33.12,0:42:36.14,Default,,0,0,0,,乘法可以被内联执行
Dialogue: 0,0:42:36.14,0:42:37.87,Default,,0,0,0,,它则会自行构建起整个机制
Dialogue: 0,0:42:38.00,0:42:39.34,Default,,0,0,0,,进行APPLY-DISPATCH
Dialogue: 0,0:42:41.37,0:42:42.49,Default,,0,0,0,,这是极大的浪费
Dialogue: 0,0:42:42.54,0:42:45.02,Default,,0,0,0,,因为 每当你进行APPLY-DISPATCH时
Dialogue: 0,0:42:45.02,0:42:46.80,Default,,0,0,0,,你都要关心这个参数表
Dialogue: 0,0:42:47.40,0:42:49.10,Default,,0,0,0,,因为它是个很普遍的操作
Dialogue: 0,0:42:49.13,0:42:51.07,Default,,0,0,0,,在任何真实的编译器中
Dialogue: 0,0:42:51.08,0:42:53.29,Default,,0,0,0,,你会有寄存器来暂存参数
Dialogue: 0,0:42:53.77,0:42:55.31,Default,,0,0,0,,你要开始保护
Dialogue: 0,0:42:56.38,0:42:58.05,Default,,0,0,0,,保存这些寄存器
Dialogue: 0,0:42:58.05,0:43:01.61,Default,,0,0,0,,和这里的策略相近
Dialogue: 0,0:43:02.85,0:43:05.93,Default,,0,0,0,,因此 我们可能主要通过这个方法
Dialogue: 0,0:43:05.95,0:43:08.30,Default,,0,0,0,,来优化书中这个特定的编译器
Dialogue: 0,0:43:08.69,0:43:09.70,Default,,0,0,0,,还有其它的一些方法
Dialogue: 0,0:43:09.70,0:43:11.82,Default,,0,0,0,,比如查找变量的值
Dialogue: 0,0:43:11.83,0:43:13.87,Default,,0,0,0,,使用更高效的基本操作
Dialogue: 0,0:43:13.88,0:43:14.56,Default,,0,0,0,,等等方法
Dialogue: 0,0:43:14.59,0:43:16.60,Default,,0,0,0,,本质上来说 一个好的Lisp编译器
Dialogue: 0,0:43:16.62,0:43:18.49,Default,,0,0,0,,可以吸收任意数量的努力
Dialogue: 0,0:43:19.72,0:43:21.63,Default,,0,0,0,,可能这其中的一个原因是
Dialogue: 0,0:43:21.89,0:43:23.04,Default,,0,0,0,,跟FORTRAN这类语言相比
Dialogue: 0,0:43:23.63,0:43:25.44,Default,,0,0,0,,Lisp就要慢一些
Dialogue: 0,0:43:25.90,0:43:28.19,Default,,0,0,0,,如果你回头审视历史
Dialogue: 0,0:43:28.22,0:43:31.12,Default,,0,0,0,,会发现人们为构建Lisp编译器而呕心沥血
Dialogue: 0,0:43:31.16,0:43:32.35,Default,,0,0,0,,但也远远没有接近
Dialogue: 0,0:43:32.36,0:43:33.90,Default,,0,0,0,,构建FORTRAN编译器的工作量
Dialogue: 0,0:43:34.43,0:43:35.79,Default,,0,0,0,,在接下来的几年
Dialogue: 0,0:43:35.92,0:43:37.68,Default,,0,0,0,,情况可能会发生变化
Dialogue: 0,0:43:38.00,0:43:38.83,Default,,0,0,0,,好吧 就讲到这里
Dialogue: 0,0:43:43.80,0:43:44.65,Default,,0,0,0,,有问题吗
Dialogue: 0,0:43:48.27,0:43:49.95,Default,,0,0,0,,学生: 很早的一个课时里--
Dialogue: 0,0:43:49.95,0:43:51.40,Default,,0,0,0,,我不记得是课上还是课后--
Dialogue: 0,0:43:51.47,0:43:53.88,Default,,0,0,0,,你向我们展示了
Dialogue: 0,0:43:54.00,0:43:57.52,Default,,0,0,0,,ADD操作有一些我们看不到的基本运算
Dialogue: 0,0:43:57.69,0:43:59.21,Default,,0,0,0,,类似于ADD%之类的
Dialogue: 0,0:43:59.82,0:44:01.65,Default,,0,0,0,,这是因为
Dialogue: 0,0:44:01.65,0:44:02.60,Default,,0,0,0,,你想把代码内联为
Dialogue: 0,0:44:02.60,0:44:08.19,Default,,0,0,0,,专门针对二元运算对象的运算么?
Dialogue: 0,0:44:08.70,0:44:10.25,Default,,0,0,0,,但如果你有更多的运算对象
Dialogue: 0,0:44:10.28,0:44:11.47,Default,,0,0,0,,你会做什么特殊的事情吗?
Dialogue: 0,0:44:12.71,0:44:16.04,Default,,0,0,0,,教授: 你看的是Scheme的实际实现
Dialogue: 0,0:44:16.06,0:44:17.84,Default,,0,0,0,,其中有一个‘+’ 这是一个运算符
Dialogue: 0,0:44:17.90,0:44:20.19,Default,,0,0,0,,如果你看‘+’的源代码
Dialogue: 0,0:44:20.33,0:44:21.37,Default,,0,0,0,,你会看到一些叫做--
Dialogue: 0,0:44:21.57,0:44:24.14,Default,,0,0,0,,我记不清了--可能叫ADD%、PLUS之类的东西
Dialogue: 0,0:44:24.55,0:44:25.79,Default,,0,0,0,,这里所进行的
Dialogue: 0,0:44:25.79,0:44:27.92,Default,,0,0,0,,就是你说的那种优化
Dialogue: 0,0:44:28.47,0:44:31.87,Default,,0,0,0,,因为 广义的‘+’接受任意数量的参数
Dialogue: 0,0:44:35.02,0:44:36.38,Default,,0,0,0,,所以 广义加法
Dialogue: 0,0:44:36.76,0:44:38.25,Default,,0,0,0,,会说:如果我有一个参数表
Dialogue: 0,0:44:38.28,0:44:40.62,Default,,0,0,0,,我最好将它们用CONS连接到表里
Dialogue: 0,0:44:41.63,0:44:44.14,Default,,0,0,0,,并指出有多少个参数
Dialogue: 0,0:44:44.72,0:44:46.16,Default,,0,0,0,,这样效率非常低下
Dialogue: 0,0:44:46.81,0:44:49.25,Default,,0,0,0,,因为大部分时间你在把两个数相加
Dialogue: 0,0:44:49.25,0:44:51.24,Default,,0,0,0,,你不必把整个参数表连接到一起
Dialogue: 0,0:44:52.04,0:44:53.93,Default,,0,0,0,,所以你想做的是
Dialogue: 0,0:44:55.66,0:44:57.71,Default,,0,0,0,,构建把一堆东西相加的代码
Dialogue: 0,0:44:58.15,0:45:00.17,Default,,0,0,0,,所以它做的大部分事情是一样的
Dialogue: 0,0:45:00.49,0:45:01.95,Default,,0,0,0,,但这里可能有个特殊的入口
Dialogue: 0,0:45:01.98,0:45:03.92,Default,,0,0,0,,如果你知道只有两个参数
Dialogue: 0,0:45:04.56,0:45:05.87,Default,,0,0,0,,你会把它们放到寄存器中
Dialogue: 0,0:45:05.87,0:45:06.97,Default,,0,0,0,,它们不需要参数表
Dialogue: 0,0:45:06.99,0:45:07.98,Default,,0,0,0,,你也不必用CONS连接它们
Dialogue: 0,0:45:08.67,0:45:10.42,Default,,0,0,0,,这就是这些东西工作的原理
Dialogue: 0,0:45:12.30,0:45:13.72,Default,,0,0,0,,好吧 下课吧
Dialogue: 0,0:45:14.10,0:45:42.97,Declare,,0,0,0,,{\fad(500,500)}MIT OpenCourseWare\Nhttp://ocw.mit.edu
Dialogue: 0,0:45:14.10,0:45:42.97,Declare,,0,0,0,,{\an2\fad(500,500)}本项目主页\Nhttps://github.com/DeathKing/Learning-SICP


================================================
FILE: Ass/lec10a.chn.ass
================================================
[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.601
PlayResX: 640
PlayResY: 480

[Aegisub Project Garbage]
Active Line: 6
Video Position: 355

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: EN,Calisto MT,21,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,1,0,2,10,10,30,1
Style: Declare,微软雅黑,30,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,8,10,10,10,1
Style: staff,微软雅黑,30,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,0,2,5,10,10,10,1
Style: title,微软雅黑,35,&H001D64D9,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,0,1,5,10,10,10,1
Style: Default,雅黑宋体,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,1,0,2,10,10,30,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.01,0:00:05.02,Declare,,0,0,0,,{\an2\fad(500,500)}Learning-SICP学习小组\N倾情制作
Dialogue: 0,0:00:05.37,0:00:11.84,staff,,0,0,0,,{\fad(600,800)\pos(110.666,403.334)}翻译&&时间轴\N杨启钊\N(windfarer)
Dialogue: 0,0:00:05.37,0:00:11.84,staff,,0,0,0,,{\fad(600,800)\pos(534.666,404)}压制&&特效\N邓雄飞\N(Dysprosium)
Dialogue: 0,0:00:05.37,0:00:11.84,staff,,0,0,0,,{\fad(600,800)\pos(574.667,277.333)}校对\N邓雄飞
Dialogue: 0,0:00:05.37,0:00:11.84,staff,,0,0,0,,{\fad(600,800)\pos(89.334,273.333)}特别感谢\N裘宗燕教授
Dialogue: 0,0:00:05.37,0:00:11.84,title,,0,0,0,,{\fad(600,800)\pos(324,32)}《计算机程序的构造和解释》
Dialogue: 0,0:00:11.84,0:00:13.84,Declare,,0,0,0,,{\an2\fad(500,500)}编译
Dialogue: 0,0:00:19.36,0:00:22.65,Default,,0,0,0,,教授: 上节课 我们学习了
Dialogue: 0,0:00:22.65,0:00:25.67,Default,,0,0,0,,一个显式控制的Lisp求值器
Dialogue: 0,0:00:25.67,0:00:28.97,Default,,0,0,0,,它弥合了像Lisp
Dialogue: 0,0:00:29.05,0:00:32.14,Default,,0,0,0,,或者查询语言之类的高级语言
Dialogue: 0,0:00:32.50,0:00:36.16,Default,,0,0,0,,与传统寄存器机器之间的鸿沟
Dialogue: 0,0:00:36.70,0:00:40.14,Default,,0,0,0,,事实上 你可以将显式控制求值器
Dialogue: 0,0:00:40.16,0:00:44.38,Default,,0,0,0,,看作是 在一台常见的
Dialogue: 0,0:00:44.40,0:00:45.95,Default,,0,0,0,,寄存机器上实现的
Dialogue: 0,0:00:46.52,0:00:49.50,Default,,0,0,0,,Lisp求值器的汇编代码
Dialogue: 0,0:00:49.50,0:00:51.50,Default,,0,0,0,,或者 你可以把它看做是
Dialogue: 0,0:00:52.08,0:00:54.56,Default,,0,0,0,,某台专门运行Lisp的机器的微程序
Dialogue: 0,0:00:55.20,0:00:55.92,Default,,0,0,0,,无论是那种情况
Dialogue: 0,0:00:55.92,0:00:58.68,Default,,0,0,0,,我们都是把一台
Dialogue: 0,0:00:58.94,0:01:00.51,Default,,0,0,0,,处理低级语言的机器
Dialogue: 0,0:01:01.42,0:01:03.32,Default,,0,0,0,,抬高到一个层次
Dialogue: 0,0:01:03.37,0:01:04.88,Default,,0,0,0,,以便处理像Lisp这样的高级语言
Dialogue: 0,0:01:05.36,0:01:06.35,Default,,0,0,0,,这是通过编写解释器来实现的
Dialogue: 0,0:01:08.22,0:01:09.58,Default,,0,0,0,,来看个例子
Dialogue: 0,0:01:11.82,0:01:13.77,Default,,0,0,0,,这里 从概念上来说
Dialogue: 0,0:01:18.01,0:01:19.47,Default,,0,0,0,,从概念上来说 这是一台
Dialogue: 0,0:01:20.54,0:01:23.44,Default,,0,0,0,,专用于计算阶乘的机器
Dialogue: 0,0:01:24.09,0:01:27.39,Default,,0,0,0,,输入5 输出120
Dialogue: 0,0:01:28.92,0:01:30.83,Default,,0,0,0,,这个专用机器实际上
Dialogue: 0,0:01:30.97,0:01:32.72,Default,,0,0,0,,是一个Lisp解释器
Dialogue: 0,0:01:33.50,0:01:36.17,Default,,0,0,0,,它将自己配置为计算阶乘
Dialogue: 0,0:01:38.35,0:01:40.99,Default,,0,0,0,,因为你向它送入了一台阶乘机器的描述
Dialogue: 0,0:01:42.12,0:01:43.70,Default,,0,0,0,,这就是解释器
Dialogue: 0,0:01:43.70,0:01:45.66,Default,,0,0,0,,它将自己配置为
Dialogue: 0,0:01:46.37,0:01:49.24,Default,,0,0,0,,模拟你所输入描述的机器
Dialogue: 0,0:01:50.07,0:01:51.93,Default,,0,0,0,,那么 在Lisp解释器里是什么?
Dialogue: 0,0:01:52.04,0:01:55.44,Default,,0,0,0,,里面可能是通用的寄存器语言解释器
Dialogue: 0,0:01:56.98,0:02:00.18,Default,,0,0,0,,它将自己配置成像Lisp解释器那样
Dialogue: 0,0:02:00.18,0:02:02.03,Default,,0,0,0,,因为你输入了一系列用寄存器语言
Dialogue: 0,0:02:02.12,0:02:03.04,Default,,0,0,0,,编写的指令
Dialogue: 0,0:02:03.37,0:02:05.16,Default,,0,0,0,,这就是显式控制求值器
Dialogue: 0,0:02:07.05,0:02:08.70,Default,,0,0,0,,它里面也有一些库
Dialogue: 0,0:02:08.73,0:02:11.08,Default,,0,0,0,,由基本运算符和Lisp运算
Dialogue: 0,0:02:11.12,0:02:12.28,Default,,0,0,0,,等等要素组成
Dialogue: 0,0:02:12.75,0:02:16.89,Default,,0,0,0,,这是解释执行的一般策略
Dialogue: 0,0:02:17.32,0:02:18.51,Default,,0,0,0,,事实上 我们所做的是
Dialogue: 0,0:02:18.60,0:02:20.14,Default,,0,0,0,,通过编写解释器
Dialogue: 0,0:02:21.62,0:02:23.40,Default,,0,0,0,,将机器抬升到
Dialogue: 0,0:02:23.42,0:02:25.24,Default,,0,0,0,,我们程序所在的层次
Dialogue: 0,0:02:25.24,0:02:26.72,Default,,0,0,0,,当然 还有另外一种策略
Dialogue: 0,0:02:27.42,0:02:28.89,Default,,0,0,0,,这种不同的策略就是编译
Dialogue: 0,0:02:29.04,0:02:30.43,Default,,0,0,0,,编译有一些不同
Dialogue: 0,0:02:31.04,0:02:31.50,Default,,0,0,0,,这里
Dialogue: 0,0:02:33.37,0:02:34.75,Default,,0,0,0,,我们可能已经实现了
Dialogue: 0,0:02:35.67,0:02:38.52,Default,,0,0,0,,一个特定用途的机器
Dialogue: 0,0:02:38.62,0:02:39.98,Default,,0,0,0,,用来计算阶乘
Dialogue: 0,0:02:43.62,0:02:46.26,Default,,0,0,0,,从某种使用寄存器语言的机器开始
Dialogue: 0,0:02:46.26,0:02:47.72,Default,,0,0,0,,但是 我们将让它执行不同的策略
Dialogue: 0,0:02:47.72,0:02:50.38,Default,,0,0,0,,把我们的阶乘程序
Dialogue: 0,0:02:51.55,0:02:53.92,Default,,0,0,0,,作为源代码输入编译器
Dialogue: 0,0:02:53.92,0:02:55.15,Default,,0,0,0,,编译器就会
Dialogue: 0,0:02:55.15,0:02:57.62,Default,,0,0,0,,把这个阶乘程序
Dialogue: 0,0:02:57.62,0:02:59.07,Default,,0,0,0,,翻译成某种寄存器机器语言
Dialogue: 0,0:03:00.25,0:03:03.40,Default,,0,0,0,,现在它并不是Lisp的显式控制求值器
Dialogue: 0,0:03:03.40,0:03:06.17,Default,,0,0,0,,而是某种用来计算阶乘的寄存器语言
Dialogue: 0,0:03:06.49,0:03:08.36,Default,,0,0,0,,这就是翻译的过程
Dialogue: 0,0:03:10.54,0:03:12.41,Default,,0,0,0,,它将进入某种加载器
Dialogue: 0,0:03:13.35,0:03:15.21,Default,,0,0,0,,它会把这些代码
Dialogue: 0,0:03:15.31,0:03:16.84,Default,,0,0,0,,和从程序库中选取的代码
Dialogue: 0,0:03:16.86,0:03:18.65,Default,,0,0,0,,比如乘法运算等 结合在一起
Dialogue: 0,0:03:19.82,0:03:21.69,Default,,0,0,0,,随后我们将生成一个加载模块
Dialogue: 0,0:03:22.22,0:03:25.06,Default,,0,0,0,,它把寄存器语言机器配置成
Dialogue: 0,0:03:25.06,0:03:27.24,Default,,0,0,0,,一个专门用来计算阶乘的机器
Dialogue: 0,0:03:28.12,0:03:30.22,Default,,0,0,0,,这就是不同的策略
Dialogue: 0,0:03:30.22,0:03:31.22,Default,,0,0,0,,在解释中
Dialogue: 0,0:03:31.22,0:03:32.01,Default,,0,0,0,,我们把机器
Dialogue: 0,0:03:32.91,0:03:35.23,Default,,0,0,0,,抬升到Lisp语言的层次
Dialogue: 0,0:03:35.32,0:03:36.34,Default,,0,0,0,,而在编译中
Dialogue: 0,0:03:36.34,0:03:38.43,Default,,0,0,0,,我们将我们的程序下降到
Dialogue: 0,0:03:38.48,0:03:40.56,Default,,0,0,0,,机器语言的层次
Dialogue: 0,0:03:41.96,0:03:43.84,Default,,0,0,0,,那么 这两个策略有什么区别呢?
Dialogue: 0,0:03:44.30,0:03:49.42,Default,,0,0,0,,编译器可以生成执行起来更有效率的代码
Dialogue: 0,0:03:52.05,0:03:53.90,Default,,0,0,0,,主要原因是
Dialogue: 0,0:03:54.17,0:03:58.89,Default,,0,0,0,,如果你考虑运行中的寄存器操作
Dialogue: 0,0:04:01.92,0:04:04.49,Default,,0,0,0,,解释器需要生成寄存器的操作
Dialogue: 0,0:04:04.97,0:04:06.75,Default,,0,0,0,,从原则上来讲 它需要足够通用
Dialogue: 0,0:04:07.32,0:04:08.94,Default,,0,0,0,,以支持任何Lisp过程的执行
Dialogue: 0,0:04:10.22,0:04:12.25,Default,,0,0,0,,而编译器只需要
Dialogue: 0,0:04:12.27,0:04:14.92,Default,,0,0,0,,生成一组特定的寄存器操作
Dialogue: 0,0:04:15.52,0:04:18.22,Default,,0,0,0,,用来执行你所编译的那部分特定的Lisp过程
Dialogue: 0,0:04:20.17,0:04:21.20,Default,,0,0,0,,换一种说法
Dialogue: 0,0:04:21.20,0:04:25.31,Default,,0,0,0,,解释器是一种通用的模拟器
Dialogue: 0,0:04:25.92,0:04:27.58,Default,,0,0,0,,当你输入一个Lisp过程时
Dialogue: 0,0:04:27.58,0:04:31.32,Default,,0,0,0,,它们就会模拟那个过程所描述的程序
Dialogue: 0,0:04:31.32,0:04:33.87,Default,,0,0,0,,所以解释器旨在成为一个通用模拟器
Dialogue: 0,0:04:34.62,0:04:35.96,Default,,0,0,0,,而编译器 实际上
Dialogue: 0,0:04:36.00,0:04:37.68,Default,,0,0,0,,只需要将东西配置成
Dialogue: 0,0:04:37.71,0:04:39.34,Default,,0,0,0,,解释器将要去模拟的机器
Dialogue: 0,0:04:40.02,0:04:41.34,Default,,0,0,0,,所以编译器可以运行得更快
Dialogue: 0,0:04:52.55,0:04:53.64,Default,,0,0,0,,另一方面
Dialogue: 0,0:04:55.97,0:04:58.28,Default,,0,0,0,,解释器更适合用来排查错误
Dialogue: 0,0:04:59.43,0:05:01.25,Default,,0,0,0,,这是因为
Dialogue: 0,0:05:01.57,0:05:03.02,Default,,0,0,0,,我们的源代码实际上就在那里
Dialogue: 0,0:05:03.02,0:05:04.81,Default,,0,0,0,,我们正在解释它们
Dialogue: 0,0:05:05.87,0:05:07.69,Default,,0,0,0,,并且库也在其中
Dialogue: 0,0:05:07.90,0:05:10.89,Default,,0,0,0,,看 库是解释器的一部分
Dialogue: 0,0:05:11.30,0:05:13.16,Default,,0,0,0,,而编译器只会拉取
Dialogue: 0,0:05:13.20,0:05:14.56,Default,,0,0,0,,运行程序所需要的代码
Dialogue: 0,0:05:14.87,0:05:17.00,Default,,0,0,0,,所以 如果你在排查错误的途中
Dialogue: 0,0:05:18.00,0:05:20.72,Default,,0,0,0,,你想写一些额外的代码
Dialogue: 0,0:05:20.80,0:05:22.57,Default,,0,0,0,,来考察运行过程中的数据类型
Dialogue: 0,0:05:23.05,0:05:24.25,Default,,0,0,0,,或者做一些
Dialogue: 0,0:05:24.30,0:05:25.92,Default,,0,0,0,,在写程序时没有想到的计算
Dialogue: 0,0:05:25.95,0:05:27.53,Default,,0,0,0,,解释器可以完美搞定这些
Dialogue: 0,0:05:28.05,0:05:29.21,Default,,0,0,0,,而编译器不行
Dialogue: 0,0:05:29.62,0:05:31.90,Default,,0,0,0,,所以它们各有优点
Dialogue: 0,0:05:31.90,0:05:34.48,Default,,0,0,0,,编译器将生成运行更快的代码
Dialogue: 0,0:05:34.85,0:05:37.02,Default,,0,0,0,,而解释器是一种更适合排错的环境
Dialogue: 0,0:05:38.95,0:05:41.40,Default,,0,0,0,,大多数Lisp系统最终将二者都实现了
Dialogue: 0,0:05:42.92,0:05:45.23,Default,,0,0,0,,这样你就可以在开发阶段
Dialogue: 0,0:05:45.24,0:05:47.08,Default,,0,0,0,,可以使用解释器
Dialogue: 0,0:05:47.08,0:05:48.62,Default,,0,0,0,,随后通过编译加速代码的运行
Dialogue: 0,0:05:49.02,0:05:50.03,Default,,0,0,0,,并且通常
Dialogue: 0,0:05:50.04,0:05:51.68,Default,,0,0,0,,你能够让被编译的代码
Dialogue: 0,0:05:51.69,0:05:53.56,Default,,0,0,0,,和被解释的代码互相调用
Dialogue: 0,0:05:54.60,0:05:56.33,Default,,0,0,0,,我们将学习如何做到 其实不难
Dialogue: 0,0:05:59.27,0:05:59.85,Default,,0,0,0,,好
Dialogue: 0,0:06:00.97,0:06:02.09,Default,,0,0,0,,事实上
Dialogue: 0,0:06:04.30,0:06:05.75,Default,,0,0,0,,在我们将要构建的编译器中
Dialogue: 0,0:06:05.75,0:06:07.58,Default,,0,0,0,,我们实现编译的代码和解释的代码
Dialogue: 0,0:06:07.58,0:06:09.45,Default,,0,0,0,,互相调用的方式是
Dialogue: 0,0:06:09.90,0:06:12.06,Default,,0,0,0,,我们让编译器和解释器使用
Dialogue: 0,0:06:12.11,0:06:14.40,Default,,0,0,0,,使用完全一致的寄存器约定
Dialogue: 0,0:06:18.42,0:06:21.72,Default,,0,0,0,,编译器的理念
Dialogue: 0,0:06:21.76,0:06:25.74,Default,,0,0,0,,与解释器或求值器的理念很像
Dialogue: 0,0:06:25.87,0:06:26.46,Default,,0,0,0,,它们是相同的
Dialogue: 0,0:06:27.05,0:06:29.39,Default,,0,0,0,,求值器遍历代码
Dialogue: 0,0:06:29.82,0:06:32.35,Default,,0,0,0,,产生一些寄存器操作
Dialogue: 0,0:06:33.65,0:06:34.97,Default,,0,0,0,,就是我们昨天做的事情
Dialogue: 0,0:06:37.10,0:06:40.27,Default,,0,0,0,,而编译器会读取代码
Dialogue: 0,0:06:40.52,0:06:43.00,Default,,0,0,0,,生成一些进行求值时
Dialogue: 0,0:06:43.04,0:06:44.67,Default,,0,0,0,,求值器会进行的
Dialogue: 0,0:06:45.23,0:06:46.64,Default,,0,0,0,,相关寄存器操作
Dialogue: 0,0:06:48.60,0:06:49.95,Default,,0,0,0,,这就给我们提供了一个模型
Dialogue: 0,0:06:50.60,0:06:53.77,Default,,0,0,0,,来实现一个零阶编译器
Dialogue: 0,0:06:55.30,0:06:58.32,Default,,0,0,0,,一个很差劲但是能用的编译器
Dialogue: 0,0:06:58.32,0:06:59.32,Default,,0,0,0,,这种模型就是
Dialogue: 0,0:06:59.36,0:07:00.59,Default,,0,0,0,,你用求值器
Dialogue: 0,0:07:00.68,0:07:01.88,Default,,0,0,0,,把代码跑一遍
Dialogue: 0,0:07:02.80,0:07:06.06,Default,,0,0,0,,但不去执行实际的操作
Dialogue: 0,0:07:06.06,0:07:07.15,Default,,0,0,0,,只是把它们保存下来
Dialogue: 0,0:07:07.55,0:07:08.82,Default,,0,0,0,,那就是你编译后的代码
Dialogue: 0,0:07:08.82,0:07:10.24,Default,,0,0,0,,让我举个例子
Dialogue: 0,0:07:12.70,0:07:14.14,Default,,0,0,0,,假设我们要编译
Dialogue: 0,0:07:15.10,0:07:17.90,Default,,0,0,0,,编译(F X) 这个表达式
Dialogue: 0,0:07:25.07,0:07:25.96,Default,,0,0,0,,我们假设
Dialogue: 0,0:07:25.96,0:07:28.06,Default,,0,0,0,,EXP寄存器中保存着(F X)
Dialogue: 0,0:07:28.06,0:07:29.55,Default,,0,0,0,,而ENV寄存器又保存着其它东西
Dialogue: 0,0:07:30.10,0:07:32.20,Default,,0,0,0,,想象我们启动了求值器
Dialogue: 0,0:07:34.60,0:07:35.71,Default,,0,0,0,,它读取了表达式
Dialogue: 0,0:07:35.71,0:07:37.36,Default,,0,0,0,,判断它是一个应用
Dialogue: 0,0:07:37.92,0:07:41.90,Default,,0,0,0,,它分支到求值器代码中的一个地方
Dialogue: 0,0:07:42.52,0:07:45.15,Default,,0,0,0,,我们之前见过的叫EV-APPLICATION的地方
Dialogue: 0,0:07:47.12,0:07:48.12,Default,,0,0,0,,然后继续处理
Dialogue: 0,0:07:48.16,0:07:50.08,Default,,0,0,0,,恢复运算对象和UNEV
Dialogue: 0,0:07:50.08,0:07:52.44,Default,,0,0,0,,然后之后它将运算符放在EXP寄存器中
Dialogue: 0,0:07:52.48,0:07:54.27,Default,,0,0,0,,递归地对它求值
Dialogue: 0,0:07:54.47,0:07:56.08,Default,,0,0,0,,这就是我们经历的过程
Dialogue: 0,0:07:56.67,0:07:57.84,Default,,0,0,0,,如果你看代码
Dialogue: 0,0:07:57.87,0:07:59.74,Default,,0,0,0,,会看到一些寄存器操作
Dialogue: 0,0:08:00.20,0:08:02.30,Default,,0,0,0,,你会看到将运算对象赋值给UNEV寄存器
Dialogue: 0,0:08:02.30,0:08:03.95,Default,,0,0,0,,把运算符赋值给EXP
Dialogue: 0,0:08:04.09,0:08:06.20,Default,,0,0,0,,保存环境、生成新环境 等等
Dialogue: 0,0:08:10.22,0:08:11.93,Default,,0,0,0,,如果我们来看下这里的投影
Dialogue: 0,0:08:15.75,0:08:19.58,Default,,0,0,0,,我们会看到产生的这些操作
Dialogue: 0,0:08:20.82,0:08:22.52,Default,,0,0,0,,这是求值器实际要进行的
Dialogue: 0,0:08:22.72,0:08:24.80,Default,,0,0,0,,第一个操作
Dialogue: 0,0:08:25.00,0:08:27.20,Default,,0,0,0,,它将运算对象从EXP寄存器里取出来
Dialogue: 0,0:08:27.47,0:08:28.62,Default,,0,0,0,,并将它赋值给UNEV
Dialogue: 0,0:08:30.03,0:08:32.27,Default,,0,0,0,,然后它给EXP寄存器赋了某个值
Dialogue: 0,0:08:32.30,0:08:33.46,Default,,0,0,0,,然后保存CONTINUE
Dialogue: 0,0:08:33.46,0:08:34.62,Default,,0,0,0,,保存ENV
Dialogue: 0,0:08:34.62,0:08:38.65,Default,,0,0,0,,我在这里就只是寄存器赋值
Dialogue: 0,0:08:39.57,0:08:42.32,Default,,0,0,0,,这就是求值器求值代码时进行的操作
Dialogue: 0,0:08:42.77,0:08:43.79,Default,,0,0,0,,我们缩小画面看看
Dialogue: 0,0:08:44.30,0:08:47.13,Default,,0,0,0,,总计有19个操作
Dialogue: 0,0:08:49.40,0:08:51.64,Default,,0,0,0,,这些代码
Dialogue: 0,0:08:52.05,0:08:53.90,Default,,0,0,0,,对应着
Dialogue: 0,0:08:54.75,0:08:57.10,Default,,0,0,0,,求值器跳转到APPLY-DISPATCH代码之前
Dialogue: 0,0:08:57.86,0:08:59.16,Default,,0,0,0,,事实上 在这个编译器中
Dialogue: 0,0:08:59.20,0:09:01.18,Default,,0,0,0,,我们不需要再关心APPLY-DISPATCH了
Dialogue: 0,0:09:01.30,0:09:02.11,Default,,0,0,0,,我们有所有东西
Dialogue: 0,0:09:02.35,0:09:05.04,Default,,0,0,0,,我们拥有解释后和编译后的所有代码
Dialogue: 0,0:09:06.07,0:09:07.61,Default,,0,0,0,,通常求值过程
Dialogue: 0,0:09:07.61,0:09:09.85,Default,,0,0,0,,是由APPLY-DISPATCH处理的
Dialogue: 0,0:09:10.27,0:09:12.32,Default,,0,0,0,,这将让被解释后代码与编译后代码
Dialogue: 0,0:09:12.36,0:09:13.71,Default,,0,0,0,,很容易互相调用
Dialogue: 0,0:09:18.27,0:09:19.87,Default,,0,0,0,,从原理上来说 这样做足矣
Dialogue: 0,0:09:21.05,0:09:22.66,Default,,0,0,0,,只需运行求值器
Dialogue: 0,0:09:22.66,0:09:24.50,Default,,0,0,0,,因而编译器非常像求值器
Dialogue: 0,0:09:24.50,0:09:26.47,Default,,0,0,0,,你运行它 唯一不同是你把操作存下来
Dialogue: 0,0:09:26.47,0:09:28.40,Default,,0,0,0,,而不是实际执行它们
Dialogue: 0,0:09:29.35,0:09:31.39,Default,,0,0,0,,这其实不完全正确
Dialogue: 0,0:09:32.91,0:09:34.99,Default,,0,0,0,,这里面我们撒了个小谎
Dialogue: 0,0:09:36.24,0:09:39.29,Default,,0,0,0,,你需要关心的是:如果有个谓词
Dialogue: 0,0:09:40.12,0:09:42.16,Default,,0,0,0,,如果你要进行某种测试
Dialogue: 0,0:09:43.45,0:09:46.03,Default,,0,0,0,,显然 在你编译时
Dialogue: 0,0:09:46.52,0:09:47.98,Default,,0,0,0,,你不知道这些分支中
Dialogue: 0,0:09:48.32,0:09:50.14,Default,,0,0,0,,哪条分支会被执行
Dialogue: 0,0:09:51.13,0:09:53.92,Default,,0,0,0,,所以你不能确定求值器将对哪个求值
Dialogue: 0,0:09:54.90,0:09:57.12,Default,,0,0,0,,因此在这里就很简单
Dialogue: 0,0:09:57.12,0:09:58.49,Default,,0,0,0,,你把两个分支全编译了
Dialogue: 0,0:09:59.32,0:10:01.29,Default,,0,0,0,,因此你编译出一个这样的结构
Dialogue: 0,0:10:02.00,0:10:03.98,Default,,0,0,0,,它们都会被编译成
Dialogue: 0,0:10:05.31,0:10:09.15,Default,,0,0,0,,首先是P的代码
Dialogue: 0,0:10:10.71,0:10:16.51,Default,,0,0,0,,它把结果存入VAL寄存器
Dialogue: 0,0:10:18.17,0:10:20.64,Default,,0,0,0,,解释器对谓词求值
Dialogue: 0,0:10:21.35,0:10:24.19,Default,,0,0,0,,并保证结果会放到VAL寄存器中
Dialogue: 0,0:10:24.70,0:10:27.22,Default,,0,0,0,,随后你编译一条指令
Dialogue: 0,0:10:27.22,0:10:33.79,Default,,0,0,0,,如果VAL是TRUE
Dialogue: 0,0:10:37.17,0:10:38.75,Default,,0,0,0,,就转到LABEL1这个地方
Dialogue: 0,0:10:44.97,0:10:47.52,Default,,0,0,0,,然后我们写下B的代码
Dialogue: 0,0:10:49.42,0:10:52.32,Default,,0,0,0,,让解释器对B进行求值
Dialogue: 0,0:10:53.62,0:10:57.21,Default,,0,0,0,,然后写一句指令
Dialogue: 0,0:10:57.23,0:10:58.75,Default,,0,0,0,,用来跳转到下一条指令
Dialogue: 0,0:11:02.20,0:11:04.56,Default,,0,0,0,,就是它结束之后要去的地方
Dialogue: 0,0:11:04.95,0:11:06.09,Default,,0,0,0,,你放入那个指令
Dialogue: 0,0:11:06.88,0:11:08.62,Default,,0,0,0,,这里你写下LABEL1
Dialogue: 0,0:11:12.12,0:11:13.80,Default,,0,0,0,,这里写A的代码
Dialogue: 0,0:11:19.47,0:11:25.85,Default,,0,0,0,,然后又是跳转到下一条指令
Dialogue: 0,0:11:31.42,0:11:32.88,Default,,0,0,0,,这就是处理条件分支的办法
Dialogue: 0,0:11:32.98,0:11:34.65,Default,,0,0,0,,你生成一小段这样的代码
Dialogue: 0,0:11:35.75,0:11:38.12,Default,,0,0,0,,除此之外
Dialogue: 0,0:11:38.95,0:11:41.55,Default,,0,0,0,,这个零阶编译器与求值器一模一样
Dialogue: 0,0:11:42.55,0:11:45.12,Default,,0,0,0,,它只是把指令存起来 而不执行它们
Dialogue: 0,0:11:46.55,0:11:47.60,Default,,0,0,0,,看起来很简单
Dialogue: 0,0:11:47.64,0:11:49.08,Default,,0,0,0,,但我们已经取得了某些收获
Dialogue: 0,0:11:50.12,0:11:52.62,Default,,0,0,0,,它会比求值器更有效率
Dialogue: 0,0:11:53.52,0:11:56.14,Default,,0,0,0,,因为 如果你观察求值器的运行
Dialogue: 0,0:11:56.35,0:12:01.05,Default,,0,0,0,,它并不只是进行寄存器操作
Dialogue: 0,0:12:01.27,0:12:03.50,Default,,0,0,0,,它还会决定执行哪个
Dialogue: 0,0:12:04.70,0:12:07.23,Default,,0,0,0,,它做的第一件事就是
Dialogue: 0,0:12:07.92,0:12:09.77,Default,,0,0,0,,以它为例 就是进行某些测试
Dialogue: 0,0:12:09.77,0:12:11.56,Default,,0,0,0,,确定它是一个应用
Dialogue: 0,0:12:13.57,0:12:15.05,Default,,0,0,0,,然后就跳转到
Dialogue: 0,0:12:15.39,0:12:16.62,Default,,0,0,0,,处理应用的地方去
Dialogue: 0,0:12:16.62,0:12:18.44,Default,,0,0,0,,换句话说 求值器做的事情是
Dialogue: 0,0:12:18.62,0:12:22.76,Default,,0,0,0,,分析代码需要进行的运算
Dialogue: 0,0:12:23.47,0:12:24.99,Default,,0,0,0,,同时并执行它们
Dialogue: 0,0:12:25.55,0:12:28.28,Default,,0,0,0,,当你运行求值器一百万次
Dialogue: 0,0:12:28.28,0:12:30.30,Default,,0,0,0,,这个分析过程就进行一百万次
Dialogue: 0,0:12:30.85,0:12:32.58,Default,,0,0,0,,而在编译器中 它只会进行一次
Dialogue: 0,0:12:32.58,0:12:34.81,Default,,0,0,0,,之后就只有寄存器操作了
Dialogue: 0,0:12:39.20,0:12:41.68,Default,,0,0,0,,这就是零阶编译器了
Dialogue: 0,0:12:41.80,0:12:44.04,Default,,0,0,0,,但它是个拙劣的编译器
Dialogue: 0,0:12:44.45,0:12:45.28,Default,,0,0,0,,它挺蠢的
Dialogue: 0,0:12:46.90,0:12:48.41,Default,,0,0,0,,让我们回过头来
Dialogue: 0,0:12:49.88,0:12:50.97,Default,,0,0,0,,看看这张投影
Dialogue: 0,0:12:52.02,0:12:55.29,Default,,0,0,0,,看看这个东西做的一些操作
Dialogue: 0,0:12:55.85,0:12:56.88,Default,,0,0,0,,我们想看看
Dialogue: 0,0:12:59.72,0:13:02.28,Default,,0,0,0,,在解释(F  X)时的操作
Dialogue: 0,0:13:03.52,0:13:04.84,Default,,0,0,0,,这里就是它做了什么
Dialogue: 0,0:13:05.17,0:13:06.11,Default,,0,0,0,,举个例子 这里
Dialogue: 0,0:13:07.15,0:13:11.98,Default,,0,0,0,,它将(OPERATOR (FETCH EXP))赋值给EXP
Dialogue: 0,0:13:13.75,0:13:15.87,Default,,0,0,0,,其实没必要这样做
Dialogue: 0,0:13:16.22,0:13:17.47,Default,,0,0,0,,因为编译器知道
Dialogue: 0,0:13:17.66,0:13:21.84,Default,,0,0,0,,(OPERATOR (FETCH EXP))的值就是F
Dialogue: 0,0:13:23.35,0:13:25.56,Default,,0,0,0,,因此这个指令没理由存在
Dialogue: 0,0:13:25.70,0:13:28.88,Default,,0,0,0,,应该改为:要把F赋值给EXP
Dialogue: 0,0:13:29.45,0:13:31.08,Default,,0,0,0,,或者实际上 你完全不需要EXP
Dialogue: 0,0:13:31.87,0:13:33.56,Default,,0,0,0,,没有理由需要EXP
Dialogue: 0,0:13:33.56,0:13:35.16,Default,,0,0,0,,EXP是用来做什么的?
Dialogue: 0,0:13:35.18,0:13:36.33,Default,,0,0,0,,我们看这里
Dialogue: 0,0:13:40.77,0:13:42.20,Default,,0,0,0,,我们对VAL赋值
Dialogue: 0,0:13:43.05,0:13:47.34,Default,,0,0,0,,在环境里的EXP里寻找东西
Dialogue: 0,0:13:48.68,0:13:49.53,Default,,0,0,0,,因此 我们实际上是要
Dialogue: 0,0:13:49.55,0:13:51.54,Default,,0,0,0,,替换掉所有的EXP寄存器
Dialogue: 0,0:13:51.54,0:13:53.32,Default,,0,0,0,,把这个指令修改为
Dialogue: 0,0:13:53.34,0:13:54.16,Default,,0,0,0,,给VAL赋值
Dialogue: 0,0:13:54.45,0:13:56.06,Default,,0,0,0,,在环境中查找
Dialogue: 0,0:13:56.36,0:13:58.40,Default,,0,0,0,,符号F的值
Dialogue: 0,0:14:01.09,0:14:01.77,Default,,0,0,0,,类似地
Dialogue: 0,0:14:02.57,0:14:04.27,Default,,0,0,0,,回到这里 我们也完全不需要UNEV
Dialogue: 0,0:14:04.72,0:14:05.79,Default,,0,0,0,,因为我们知道
Dialogue: 0,0:14:06.22,0:14:09.16,Default,,0,0,0,,因为我们知道 (FETCH EXP)取出的运算对象
Dialogue: 0,0:14:09.16,0:14:10.62,Default,,0,0,0,,就是'(X)
Dialogue: 0,0:14:13.25,0:14:14.06,Default,,0,0,0,,从某种意义上来说
Dialogue: 0,0:14:16.17,0:14:19.39,Default,,0,0,0,,你完全不需要UNEV和EXP
Dialogue: 0,0:14:19.67,0:14:21.05,Default,,0,0,0,,看看它们实际上是什么
Dialogue: 0,0:14:21.08,0:14:25.30,Default,,0,0,0,,它们不是实际运行机器的寄存器
Dialogue: 0,0:14:25.30,0:14:26.40,Default,,0,0,0,,它们实际上是
Dialogue: 0,0:14:26.60,0:14:29.50,Default,,0,0,0,,为了模拟该机器的而设置的寄存器
Dialogue: 0,0:14:30.72,0:14:33.77,Default,,0,0,0,,所以它们保存一些表达式
Dialogue: 0,0:14:34.00,0:14:36.04,Default,,0,0,0,,以编译器的视角看来
Dialogue: 0,0:14:36.06,0:14:36.81,Default,,0,0,0,,它们就是常量
Dialogue: 0,0:14:36.95,0:14:38.48,Default,,0,0,0,,因此可以直接把它们放到代码中
Dialogue: 0,0:14:39.47,0:14:41.34,Default,,0,0,0,,你可以忘掉那些关于
Dialogue: 0,0:14:41.36,0:14:42.54,Default,,0,0,0,,EXP和UNEV的操作
Dialogue: 0,0:14:42.57,0:14:43.77,Default,,0,0,0,,只用那些常量
Dialogue: 0,0:14:44.02,0:14:48.00,Default,,0,0,0,,与之相似 如果我们回顾这里
Dialogue: 0,0:14:48.00,0:14:51.32,Default,,0,0,0,,有像(ASSIGN CONTINUE EVAL-ARGS)之类的语句
Dialogue: 0,0:14:53.75,0:14:55.39,Default,,0,0,0,,现在 它和任何东西都没有关系
Dialogue: 0,0:14:55.62,0:14:57.76,Default,,0,0,0,,它只是求值器
Dialogue: 0,0:14:58.08,0:15:00.17,Default,,0,0,0,,维护了下一步需要去哪
Dialogue: 0,0:15:02.70,0:15:05.96,Default,,0,0,0,,在某些应用中对参数进行求值
Dialogue: 0,0:15:06.82,0:15:08.65,Default,,0,0,0,,当然 这与编译器没关系
Dialogue: 0,0:15:08.65,0:15:13.88,Default,,0,0,0,,因为这个分析过程已经被编译器做完了
Dialogue: 0,0:15:15.05,0:15:16.83,Default,,0,0,0,,所以编译后的代码完全不需要它
Dialogue: 0,0:15:17.70,0:15:19.32,Default,,0,0,0,,因此许多向CONTINUE寄存器
Dialogue: 0,0:15:19.32,0:15:21.30,Default,,0,0,0,,赋值的操作都是无用的
Dialogue: 0,0:15:21.30,0:15:24.62,Default,,0,0,0,,运行着的机器留着它们
Dialogue: 0,0:15:24.64,0:15:25.77,Default,,0,0,0,,是为了跟踪它的状态
Dialogue: 0,0:15:26.07,0:15:28.72,Default,,0,0,0,,是为了知道求值器的下一步分析
Dialogue: 0,0:15:28.72,0:15:30.03,Default,,0,0,0,,而它们是完全无关的
Dialogue: 0,0:15:30.06,0:15:31.23,Default,,0,0,0,,因此我们可以去掉它们
Dialogue: 0,0:15:43.90,0:15:45.98,Default,,0,0,0,,那么 如果我们简单地
Dialogue: 0,0:15:46.16,0:15:47.75,Default,,0,0,0,,进行这类优化
Dialogue: 0,0:15:47.75,0:15:51.64,Default,,0,0,0,,不再考虑EXP和UNEV
Dialogue: 0,0:15:51.75,0:15:56.22,Default,,0,0,0,,去掉这些无关的寄存器赋值
Dialogue: 0,0:15:57.25,0:15:59.96,Default,,0,0,0,,我们就可以找到这些代码
Dialogue: 0,0:16:01.48,0:16:06.20,Default,,0,0,0,,也就是求值器会执行的这19条指令
Dialogue: 0,0:16:06.91,0:16:08.12,Default,,0,0,0,,给替换掉
Dialogue: 0,0:16:08.36,0:16:10.33,Default,,0,0,0,,请看幻灯片
Dialogue: 0,0:16:12.27,0:16:15.34,Default,,0,0,0,,我们去掉了大概一半
Dialogue: 0,0:16:18.28,0:16:20.75,Default,,0,0,0,,同样 这就是某种过滤
Dialogue: 0,0:16:21.07,0:16:24.46,Default,,0,0,0,,把无关的东西去掉
Dialogue: 0,0:16:25.17,0:16:26.22,Default,,0,0,0,,你们看 比如说
Dialogue: 0,0:16:27.47,0:16:29.66,Default,,0,0,0,,这里 求值器说
Dialogue: 0,0:16:29.68,0:16:32.43,Default,,0,0,0,,(ASSIGN VAL (LOOKUP 'F (FETCH ENV)))
Dialogue: 0,0:16:32.46,0:16:34.22,Default,,0,0,0,,这里 我们放入了一个常量F
Dialogue: 0,0:16:35.44,0:16:37.02,Default,,0,0,0,,这里又放了一个常量X
Dialogue: 0,0:16:40.02,0:16:42.41,Default,,0,0,0,,因此 这个编译器又稍微好一点
Dialogue: 0,0:16:43.79,0:16:46.76,Default,,0,0,0,,但它还是比较蠢
Dialogue: 0,0:16:47.95,0:16:49.58,Default,,0,0,0,,它仍会做很多蠢事
Dialogue: 0,0:16:50.45,0:16:52.52,Default,,0,0,0,,我们再看幻灯片
Dialogue: 0,0:16:52.88,0:16:53.93,Default,,0,0,0,,看最开头的地方
Dialogue: 0,0:16:56.34,0:16:58.17,Default,,0,0,0,,我们调用(SAVE ENV)保存环境
Dialogue: 0,0:16:59.35,0:17:01.72,Default,,0,0,0,,然后给VAL寄存器赋某个值
Dialogue: 0,0:17:01.80,0:17:03.35,Default,,0,0,0,,然后恢复环境
Dialogue: 0,0:17:03.35,0:17:04.41,Default,,0,0,0,,它是从哪来的
Dialogue: 0,0:17:04.91,0:17:07.10,Default,,0,0,0,,它来自求值器的这个地方
Dialogue: 0,0:17:07.15,0:17:10.28,Default,,0,0,0,,哦 我在正在对一个应用求值
Dialogue: 0,0:17:11.10,0:17:14.68,Default,,0,0,0,,因此我要递归调用EVAL-DISPATCH
Dialogue: 0,0:17:15.87,0:17:17.98,Default,,0,0,0,,我最好把接下来要用到的东西
Dialogue: 0,0:17:17.98,0:17:19.08,Default,,0,0,0,,保存到环境中
Dialogue: 0,0:17:19.77,0:17:22.86,Default,,0,0,0,,这就是递归调用EVAL-DISPATCH的结果
Dialogue: 0,0:17:23.47,0:17:25.77,Default,,0,0,0,,刚才那个例子就是对符号F求值的结果
Dialogue: 0,0:17:26.50,0:17:28.27,Default,,0,0,0,,从EVAL-DISPATCH中返回
Dialogue: 0,0:17:28.28,0:17:29.66,Default,,0,0,0,,将环境恢复
Dialogue: 0,0:17:31.25,0:17:32.28,Default,,0,0,0,,但是实际上
Dialogue: 0,0:17:32.59,0:17:35.88,Default,,0,0,0,,这个求值过程中 所进行的操作
Dialogue: 0,0:17:35.92,0:17:37.71,Default,,0,0,0,,完全不会影响环境
Dialogue: 0,0:17:38.67,0:17:40.80,Default,,0,0,0,,所以这里没必要先保存环境
Dialogue: 0,0:17:40.84,0:17:42.22,Default,,0,0,0,,再恢复环境
Dialogue: 0,0:17:45.67,0:17:46.62,Default,,0,0,0,,与之类似
Dialogue: 0,0:17:49.79,0:17:51.39,Default,,0,0,0,,这里 我们保存了参数表
Dialogue: 0,0:17:53.07,0:17:55.80,Default,,0,0,0,,那是一个求值参数的循环
Dialogue: 0,0:17:55.82,0:17:56.86,Default,,0,0,0,,先保存参数表
Dialogue: 0,0:17:57.20,0:17:58.03,Default,,0,0,0,,然后在这里恢复
Dialogue: 0,0:17:58.08,0:18:00.51,Default,,0,0,0,,但事实上最后
Dialogue: 0,0:18:00.80,0:18:02.28,Default,,0,0,0,,并没有变更参数表
Dialogue: 0,0:18:02.84,0:18:04.17,Default,,0,0,0,,所以不需要保存它
Dialogue: 0,0:18:08.65,0:18:12.88,Default,,0,0,0,,换种方式来说
Dialogue: 0,0:18:13.77,0:18:14.80,Default,,0,0,0,,怎么说呢
Dialogue: 0,0:18:16.43,0:18:19.13,Default,,0,0,0,,求值器需要最大限度地保持悲观
Dialogue: 0,0:18:19.87,0:18:21.07,Default,,0,0,0,,因为 从它的视角来看
Dialogue: 0,0:18:21.08,0:18:23.06,Default,,0,0,0,,只知道接下来是要对某些东西进行求值
Dialogue: 0,0:18:23.24,0:18:24.97,Default,,0,0,0,,所以最好把稍后要用的都存下来
Dialogue: 0,0:18:26.12,0:18:27.79,Default,,0,0,0,,一旦你完成了分析
Dialogue: 0,0:18:27.82,0:18:29.68,Default,,0,0,0,,从编译器的角度就会考虑
Dialogue: 0,0:18:29.72,0:18:31.47,Default,,0,0,0,,哪些是我真正需要存下来的?
Dialogue: 0,0:18:32.12,0:18:33.31,Default,,0,0,0,,我们需要去 --
Dialogue: 0,0:18:33.42,0:18:37.30,Default,,0,0,0,,它不需要像求值器一样小心翼翼
Dialogue: 0,0:18:37.30,0:18:38.80,Default,,0,0,0,,因为它知道 实际需要什么
Dialogue: 0,0:18:39.69,0:18:41.16,Default,,0,0,0,,无论如何 如果我们完成了优化
Dialogue: 0,0:18:42.50,0:18:45.71,Default,,0,0,0,,消除掉所有多余的保存和恢复
Dialogue: 0,0:18:46.40,0:18:49.05,Default,,0,0,0,,那么我们可以得到这样的结果
Dialogue: 0,0:18:49.90,0:18:51.53,Default,,0,0,0,,我们可以发现
Dialogue: 0,0:18:51.64,0:18:53.71,Default,,0,0,0,,只有三条指令是必须的
Dialogue: 0,0:18:54.07,0:18:55.72,Default,,0,0,0,,从刚才的11条指令优化成这样
Dialogue: 0,0:18:55.97,0:18:58.81,Default,,0,0,0,,或是从原始的20条指令优化而来
Dialogue: 0,0:18:59.87,0:19:00.92,Default,,0,0,0,,这告诉我们
Dialogue: 0,0:19:01.12,0:19:03.18,Default,,0,0,0,,对于这些寄存器操作
Dialogue: 0,0:19:03.27,0:19:04.94,Default,,0,0,0,,哪些是必需的?
Dialogue: 0,0:19:09.42,0:19:11.74,Default,,0,0,0,,让我换个方式来总结一下
Dialogue: 0,0:19:11.74,0:19:13.48,Default,,0,0,0,,我先给你们看一张图
Dialogue: 0,0:19:16.00,0:19:17.52,Default,,0,0,0,,这张图片
Dialogue: 0,0:19:18.77,0:19:20.81,Default,,0,0,0,,展示了所有的保存和恢复
Dialogue: 0,0:19:23.50,0:19:25.23,Default,,0,0,0,,这里是表达式(F X)
Dialogue: 0,0:19:25.32,0:19:27.87,Default,,0,0,0,,在下面这里
Dialogue: 0,0:19:28.75,0:19:31.80,Default,,0,0,0,,是对求值器中各种地方的跟踪
Dialogue: 0,0:19:34.97,0:19:38.04,Default,,0,0,0,,在求值发生时会使用这些地方
Dialogue: 0,0:19:38.04,0:19:40.01,Default,,0,0,0,,在这里 你可以看到箭头
Dialogue: 0,0:19:40.22,0:19:42.08,Default,,0,0,0,,下箭头代表寄存器的保存
Dialogue: 0,0:19:42.40,0:19:44.84,Default,,0,0,0,,所以最先保存的是ENV寄存器
Dialogue: 0,0:19:46.82,0:19:48.68,Default,,0,0,0,,然后 在这里恢复ENV
Dialogue: 0,0:19:52.38,0:19:54.54,Default,,0,0,0,,这些都是成对的栈操作
Dialogue: 0,0:19:56.12,0:19:57.56,Default,,0,0,0,,如果你更进一步
Dialogue: 0,0:19:58.12,0:20:00.78,Default,,0,0,0,,我们记得
Dialogue: 0,0:20:00.89,0:20:03.02,Default,,0,0,0,,UNEV是个完全没用的寄存器
Dialogue: 0,0:20:07.80,0:20:09.78,Default,,0,0,0,,如果我们用固定结构的代码
Dialogue: 0,0:20:09.78,0:20:12.52,Default,,0,0,0,,就不需要保存UNEV 因为完全用不上
Dialogue: 0,0:20:16.20,0:20:19.15,Default,,0,0,0,,然后 根据我们约定的
Dialogue: 0,0:20:19.16,0:20:21.88,Default,,0,0,0,,应用过程的准则
Dialogue: 0,0:20:21.88,0:20:23.85,Default,,0,0,0,,我们会选择是否保存CONTINUE
Dialogue: 0,0:20:27.40,0:20:28.74,Default,,0,0,0,,这就是我们做的第一件事
Dialogue: 0,0:20:28.74,0:20:30.51,Default,,0,0,0,,然后我们可以看看
Dialogue: 0,0:20:31.71,0:20:32.70,Default,,0,0,0,,实际需要些什么
Dialogue: 0,0:20:33.07,0:20:35.56,Default,,0,0,0,,其实在求值F的过程中
Dialogue: 0,0:20:36.04,0:20:37.82,Default,,0,0,0,,我们不需要保存ENV
Dialogue: 0,0:20:38.08,0:20:39.92,Default,,0,0,0,,因为它不会被破坏
Dialogue: 0,0:20:39.92,0:20:41.31,Default,,0,0,0,,因此 如果我们利用这点
Dialogue: 0,0:20:44.12,0:20:47.56,Default,,0,0,0,,这里对F的求值
Dialogue: 0,0:20:48.57,0:20:50.44,Default,,0,0,0,,完全不需要担心
Dialogue: 0,0:20:51.61,0:20:52.60,Default,,0,0,0,,会破坏ENV
Dialogue: 0,0:20:52.60,0:20:54.94,Default,,0,0,0,,类似地 这里对X的求值
Dialogue: 0,0:20:57.17,0:20:58.89,Default,,0,0,0,,当求值器进行求值时 它会说
Dialogue: 0,0:20:58.91,0:21:01.64,Default,,0,0,0,,我最好保存好与之有关的FUN寄存器
Dialogue: 0,0:21:02.07,0:21:03.22,Default,,0,0,0,,因为后面也许会用得着
Dialogue: 0,0:21:03.28,0:21:04.89,Default,,0,0,0,,我最好也保存参数表
Dialogue: 0,0:21:06.90,0:21:09.05,Default,,0,0,0,,然而 在这如果是编译器的话
Dialogue: 0,0:21:09.05,0:21:10.38,Default,,0,0,0,,实际需要哪些寄存器
Dialogue: 0,0:21:10.52,0:21:11.84,Default,,0,0,0,,从而进行相关的保存与恢复
Dialogue: 0,0:21:12.70,0:21:16.09,Default,,0,0,0,,事实上 这里求值器做的所有栈操作
Dialogue: 0,0:21:16.32,0:21:19.58,Default,,0,0,0,,都证明是过于悲观而不必要
Dialogue: 0,0:21:19.62,0:21:21.45,Default,,0,0,0,,而编译器在这里是知道这一点的
Dialogue: 0,0:21:27.35,0:21:28.48,Default,,0,0,0,,这是最基础的想法
Dialogue: 0,0:21:29.80,0:21:31.00,Default,,0,0,0,,我们把求值器
Dialogue: 0,0:21:31.00,0:21:33.24,Default,,0,0,0,,剔除那些不需要的东西
Dialogue: 0,0:21:33.24,0:21:35.24,Default,,0,0,0,,去除那些对于编译器完全无用的东西
Dialogue: 0,0:21:35.24,0:21:36.19,Default,,0,0,0,,只保留求值的部分
Dialogue: 0,0:21:37.40,0:21:40.40,Default,,0,0,0,,然后你可以看到哪些栈操作是不必要的
Dialogue: 0,0:21:40.82,0:21:43.76,Default,,0,0,0,,这就是书中所描述的编译器
Dialogue: 0,0:21:43.85,0:21:45.04,Default,,0,0,0,,的基本结构
Dialogue: 0,0:21:45.04,0:21:47.00,Default,,0,0,0,,我给你们展示一下这
Dialogue: 0,0:21:47.76,0:21:49.68,Default,,0,0,0,,这个简单的例子
Dialogue: 0,0:21:51.20,0:21:53.26,Default,,0,0,0,,为了说清楚 多余的东西是怎样保存的
Dialogue: 0,0:21:53.29,0:21:56.06,Default,,0,0,0,,我们来看一个稍复杂的表达式
Dialogue: 0,0:21:58.15,0:22:01.93,Default,,0,0,0,,(F (G X) 1)
Dialogue: 0,0:22:03.87,0:22:05.52,Default,,0,0,0,,我们不会讲解所有的代码
Dialogue: 0,0:22:06.40,0:22:08.56,Default,,0,0,0,,因为代码有点多
Dialogue: 0,0:22:09.72,0:22:12.35,Default,,0,0,0,,我认为在求值器在处理它时
Dialogue: 0,0:22:12.35,0:22:14.67,Default,,0,0,0,,大概会产生
Dialogue: 0,0:22:14.70,0:22:16.25,Default,,0,0,0,,16对保存-恢复操作
Dialogue: 0,0:22:17.00,0:22:18.57,Default,,0,0,0,,这有一张图表
Dialogue: 0,0:22:20.57,0:22:21.95,Default,,0,0,0,,演示了其中的过程
Dialogue: 0,0:22:22.97,0:22:23.90,Default,,0,0,0,,你从这里开始--
Dialogue: 0,0:22:24.25,0:22:26.62,Default,,0,0,0,,求值器说:“我要求值一个应用”
Dialogue: 0,0:22:26.90,0:22:29.13,Default,,0,0,0,,在这里保存ENV 又在这里恢复
Dialogue: 0,0:22:30.65,0:22:34.44,Default,,0,0,0,,然后处理第一个运算对象
Dialogue: 0,0:22:36.81,0:22:39.28,Default,,0,0,0,,这是求值器的递归调用
Dialogue: 0,0:22:39.28,0:22:40.89,Default,,0,0,0,,求值器发现 这是一个应用
Dialogue: 0,0:22:40.91,0:22:42.10,Default,,0,0,0,,又会保存环境
Dialogue: 0,0:22:42.10,0:22:44.97,Default,,0,0,0,,求值组合式的运算符 然后在这里恢复环境
Dialogue: 0,0:22:45.80,0:22:48.92,Default,,0,0,0,,这个恢复匹配的是这个保存操作
Dialogue: 0,0:22:49.77,0:22:50.78,Default,,0,0,0,,以此类推
Dialogue: 0,0:22:51.65,0:22:52.51,Default,,0,0,0,,这里的UNEV
Dialogue: 0,0:22:52.52,0:22:54.62,Default,,0,0,0,,完全没有必要存在
Dialogue: 0,0:22:54.97,0:22:56.60,Default,,0,0,0,,CONTINUE寄存器不断地被保存-恢复
Dialogue: 0,0:22:57.42,0:23:00.41,Default,,0,0,0,,而FUN寄存器则是在
Dialogue: 0,0:23:00.78,0:23:04.36,Default,,0,0,0,,处理运算对象期间被保存
Dialogue: 0,0:23:05.10,0:23:06.52,Default,,0,0,0,,这类的事情一直在发生
Dialogue: 0,0:23:06.78,0:23:09.39,Default,,0,0,0,,但如果你问 跟求值器相比
Dialogue: 0,0:23:09.87,0:23:11.66,Default,,0,0,0,,编译器究竟要做什么?
Dialogue: 0,0:23:12.27,0:23:13.55,Default,,0,0,0,,你会去掉一大堆东西
Dialogue: 0,0:23:14.30,0:23:16.64,Default,,0,0,0,,在这个的基础上 如果你说
Dialogue: 0,0:23:19.40,0:23:22.54,Default,,0,0,0,,对F的求值不会修改ENV寄存器
Dialogue: 0,0:23:23.82,0:23:26.51,Default,,0,0,0,,或者对符号X的查找
Dialogue: 0,0:23:29.28,0:23:32.09,Default,,0,0,0,,不需要特别保护FUN寄存器
Dialogue: 0,0:23:34.30,0:23:37.60,Default,,0,0,0,,就得到了只有几对的保存-恢复操作
Dialogue: 0,0:23:40.25,0:23:42.27,Default,,0,0,0,,然而 你还可以再优化一下
Dialogue: 0,0:23:42.27,0:23:44.33,Default,,0,0,0,,看看这里的ENV寄存器发生了什么
Dialogue: 0,0:23:45.21,0:23:47.39,Default,,0,0,0,,我们观察ENV寄存器的操作 发现
Dialogue: 0,0:23:51.00,0:23:52.25,Default,,0,0,0,,这是一个组合式
Dialogue: 0,0:23:54.33,0:23:55.69,Default,,0,0,0,,而这个求值器
Dialogue: 0,0:23:55.78,0:23:57.27,Default,,0,0,0,,对G一无所知
Dialogue: 0,0:23:58.57,0:24:00.73,Default,,0,0,0,,所以在这 它说
Dialogue: 0,0:24:01.29,0:24:03.45,Default,,0,0,0,,我最好保存ENV寄存器
Dialogue: 0,0:24:03.96,0:24:05.42,Default,,0,0,0,,因为对G的求值
Dialogue: 0,0:24:05.42,0:24:07.42,Default,,0,0,0,,可能会修改ENV寄存器的值
Dialogue: 0,0:24:07.55,0:24:09.45,Default,,0,0,0,,而我稍后可能会需要它
Dialogue: 0,0:24:10.17,0:24:11.40,Default,,0,0,0,,在这个参数之后
Dialogue: 0,0:24:12.22,0:24:13.37,Default,,0,0,0,,在处理第二个参数的时候
Dialogue: 0,0:24:15.60,0:24:17.24,Default,,0,0,0,,这就是为什么它没被优化掉
Dialogue: 0,0:24:19.07,0:24:22.54,Default,,0,0,0,,因为编译器没有对G将要做的事情做任何假设
Dialogue: 0,0:24:22.54,0:24:23.60,Default,,0,0,0,,另一方面
Dialogue: 0,0:24:24.61,0:24:26.52,Default,,0,0,0,,如果你看看这里的第二个参数
Dialogue: 0,0:24:26.64,0:24:27.70,Default,,0,0,0,,它只是查找“1”这个常量
Dialogue: 0,0:24:27.70,0:24:29.60,Default,,0,0,0,,这不需要ENV寄存器
Dialogue: 0,0:24:30.77,0:24:32.04,Default,,0,0,0,,因此没必要保存它
Dialogue: 0,0:24:32.06,0:24:33.77,Default,,0,0,0,,事实上 你也可以把这个也去掉
Dialogue: 0,0:24:34.85,0:24:37.81,Default,,0,0,0,,这一堆寄存器操作
Dialogue: 0,0:24:37.98,0:24:40.08,Default,,0,0,0,,如果你像这样简单地推理的话
Dialogue: 0,0:24:40.55,0:24:43.05,Default,,0,0,0,,只会剩下两对保存-恢复操作
Dialogue: 0,0:24:45.10,0:24:46.97,Default,,0,0,0,,而这些 如果你知道关于G的某些信息的话
Dialogue: 0,0:24:47.52,0:24:49.08,Default,,0,0,0,,可以进一步优化
Dialogue: 0,0:24:56.27,0:24:57.85,Default,,0,0,0,,基本的理念是
Dialogue: 0,0:24:57.95,0:24:59.98,Default,,0,0,0,,编译器之所以更好
Dialogue: 0,0:24:59.98,0:25:02.56,Default,,0,0,0,,是因为解释器对于将要处理的东西一无所知
Dialogue: 0,0:25:03.25,0:25:05.04,Default,,0,0,0,,它不得不以最悲观的方式保存东西
Dialogue: 0,0:25:05.05,0:25:06.70,Default,,0,0,0,,来保护它自己
Dialogue: 0,0:25:07.90,0:25:08.76,Default,,0,0,0,,而编译器
Dialogue: 0,0:25:09.48,0:25:12.38,Default,,0,0,0,,只需要保存实际需要的东西
Dialogue: 0,0:25:13.37,0:25:15.20,Default,,0,0,0,,某个东西是否需要保存
Dialogue: 0,0:25:15.24,0:25:17.37,Default,,0,0,0,,有两种原因
Dialogue: 0,0:25:17.82,0:25:18.70,Default,,0,0,0,,一种是
Dialogue: 0,0:25:18.70,0:25:19.82,Default,,0,0,0,,你保护的东西
Dialogue: 0,0:25:19.95,0:25:21.44,Default,,0,0,0,,不会修改寄存器
Dialogue: 0,0:25:22.08,0:25:23.58,Default,,0,0,0,,例如 变量查找
Dialogue: 0,0:25:24.12,0:25:25.20,Default,,0,0,0,,另一种原因是
Dialogue: 0,0:25:25.32,0:25:27.10,Default,,0,0,0,,你所保存的东西
Dialogue: 0,0:25:28.28,0:25:29.92,Default,,0,0,0,,最后并不会被用到
Dialogue: 0,0:25:30.81,0:25:34.27,Default,,0,0,0,,因此 编译器正是利用了
Dialogue: 0,0:25:34.30,0:25:35.88,Default,,0,0,0,,这两条基本原则
Dialogue: 0,0:25:36.27,0:25:37.76,Default,,0,0,0,,来让代码变得更高效的
Dialogue: 0,0:25:44.27,0:25:45.32,Default,,0,0,0,,有什么问题吗?
Dialogue: 0,0:25:51.20,0:25:53.10,Default,,0,0,0,,学生: 你一直在说UNEV寄存器
Dialogue: 0,0:25:53.13,0:25:56.40,Default,,0,0,0,,UNEV寄存器完全不会被用到
Dialogue: 0,0:25:56.41,0:25:58.68,Default,,0,0,0,,是否意味着 机器只需要6个寄存器足矣?
Dialogue: 0,0:25:58.70,0:26:00.08,Default,,0,0,0,,或者是说 在这个特定的例子里
Dialogue: 0,0:26:00.11,0:26:01.18,Default,,0,0,0,,它没有被用到?
Dialogue: 0,0:26:01.72,0:26:02.81,Default,,0,0,0,,教授: 对于编译器
Dialogue: 0,0:26:04.31,0:26:07.42,Default,,0,0,0,,你可以生成6个或5个寄存器的代码
Dialogue: 0,0:26:07.56,0:26:09.02,Default,,0,0,0,,因为EXP寄存器也没有用到
Dialogue: 0,0:26:09.40,0:26:14.57,Default,,0,0,0,,是的 你可以把EXP和UNEV都去掉
Dialogue: 0,0:26:14.57,0:26:16.87,Default,,0,0,0,,因为这些是求值器的数据结构
Dialogue: 0,0:26:17.36,0:26:19.36,Default,,0,0,0,,以编译器的视角来看
Dialogue: 0,0:26:19.39,0:26:20.87,Default,,0,0,0,,这些东西都是常量
Dialogue: 0,0:26:21.65,0:26:22.44,Default,,0,0,0,,关键在于
Dialogue: 0,0:26:22.48,0:26:24.59,Default,,0,0,0,,这个特定编译器是被构造出来的
Dialogue: 0,0:26:24.79,0:26:27.92,Default,,0,0,0,,因此被解释的代码和被编译的代码可以共存
Dialogue: 0,0:26:29.32,0:26:30.72,Default,,0,0,0,,可以这样看待它
Dialogue: 0,0:26:30.97,0:26:32.29,Default,,0,0,0,,你构建了一个芯片
Dialogue: 0,0:26:34.30,0:26:35.50,Default,,0,0,0,,它就是求值器
Dialogue: 0,0:26:35.88,0:26:37.28,Default,,0,0,0,,而编译器可以做的就是
Dialogue: 0,0:26:37.31,0:26:39.02,Default,,0,0,0,,为这个芯片生成代码
Dialogue: 0,0:26:40.40,0:26:41.90,Default,,0,0,0,,只是它不会用到两个寄存器而已
Dialogue: 0,0:26:51.52,0:26:52.47,Default,,0,0,0,,好 休息一会
Dialogue: 0,0:26:53.55,0:27:07.18,Default,,0,0,0,,[音乐]
Dialogue: 0,0:27:07.37,0:27:11.42,Declare,,0,0,0,,{\an2\fad(500,500)}《计算机程序的构造和解释》
Dialogue: 0,0:27:14.57,0:27:18.12,Declare,,0,0,0,,{\an2\fad(500,500)}讲师: 哈罗德·艾伯森教授 及 格兰德·杰·萨斯曼教授
Dialogue: 0,0:27:18.17,0:27:22.08,Declare,,0,0,0,,{\an2\fad(500,500)}《计算机程序的构造和解释》
Dialogue: 0,0:27:22.22,0:27:26.48,Declare,,0,0,0,,{\an2\fad(500,500)}编译
Dialogue: 0,0:27:29.21,0:27:32.43,Default,,0,0,0,,我们刚才研究了编译器应该要做什么
Dialogue: 0,0:27:32.78,0:27:36.04,Default,,0,0,0,,现在我们来简略地看看
Dialogue: 0,0:27:36.15,0:27:37.47,Default,,0,0,0,,这些目标如何达成
Dialogue: 0,0:27:38.26,0:27:39.58,Default,,0,0,0,,而我不会给出细节
Dialogue: 0,0:27:39.60,0:27:42.17,Default,,0,0,0,,在书中有一大堆代码
Dialogue: 0,0:27:42.22,0:27:43.42,Default,,0,0,0,,展示了所有细节
Dialogue: 0,0:27:43.45,0:27:45.31,Default,,0,0,0,,我要做的 是给你们展示
Dialogue: 0,0:27:45.96,0:27:47.26,Default,,0,0,0,,其中的关键思想
Dialogue: 0,0:27:49.49,0:27:51.36,Default,,0,0,0,,换个时间再来关心细节
Dialogue: 0,0:27:51.51,0:27:55.30,Default,,0,0,0,,设想我们正在编译一条表达式
Dialogue: 0,0:27:55.30,0:27:57.01,Default,,0,0,0,,这里有一些运算符
Dialogue: 0,0:27:57.48,0:27:58.56,Default,,0,0,0,,和两个参数
Dialogue: 0,0:28:03.56,0:28:04.24,Default,,0,0,0,,现在
Dialogue: 0,0:28:06.27,0:28:08.14,Default,,0,0,0,,这个编译器会生成什么代码?
Dialogue: 0,0:28:08.85,0:28:09.78,Default,,0,0,0,,首先
Dialogue: 0,0:28:09.83,0:28:11.20,Default,,0,0,0,,它会递归运行
Dialogue: 0,0:28:11.90,0:28:13.28,Default,,0,0,0,,编译运算符
Dialogue: 0,0:28:14.37,0:28:19.02,Default,,0,0,0,,它说 我要编译运算符
Dialogue: 0,0:28:21.16,0:28:24.54,Default,,0,0,0,,最后我需要让它们的结果
Dialogue: 0,0:28:24.84,0:28:27.95,Default,,0,0,0,,存放在FUN寄存器中
Dialogue: 0,0:28:28.42,0:28:29.60,Default,,0,0,0,,所以我编译一些指令
Dialogue: 0,0:28:29.64,0:28:31.56,Default,,0,0,0,,它们会编译运算符
Dialogue: 0,0:28:31.69,0:28:38.62,Default,,0,0,0,,最后把结果放在FUN寄存器中
Dialogue: 0,0:28:45.51,0:28:46.94,Default,,0,0,0,,接下来我要做的是
Dialogue: 0,0:28:47.71,0:28:49.68,Default,,0,0,0,,另一个代码片段则说
Dialogue: 0,0:28:49.68,0:28:55.17,Default,,0,0,0,,我要编译第一个参数
Dialogue: 0,0:28:55.17,0:28:56.80,Default,,0,0,0,,因此它递归调地用自己
Dialogue: 0,0:28:58.04,0:29:03.36,Default,,0,0,0,,而结果会被放在VAL中
Dialogue: 0,0:29:09.07,0:29:10.75,Default,,0,0,0,,接下来需要做的是
Dialogue: 0,0:29:10.75,0:29:12.26,Default,,0,0,0,,建立起参数表
Dialogue: 0,0:29:12.95,0:29:25.50,Default,,0,0,0,,(ASSIGN ARGL (CONS (FETCH --
Dialogue: 0,0:29:25.55,0:29:27.10,Default,,0,0,0,,它会生成这些代码
Dialogue: 0,0:29:27.50,0:29:32.51,Default,,0,0,0,,(FETCH VAL) '()))
Dialogue: 0,0:29:35.00,0:29:36.05,Default,,0,0,0,,然而
Dialogue: 0,0:29:37.99,0:29:40.61,Default,,0,0,0,,当它到这里时
Dialogue: 0,0:29:41.32,0:29:42.82,Default,,0,0,0,,它可能需要环境
Dialogue: 0,0:29:43.95,0:29:45.29,Default,,0,0,0,,它需要环境
Dialogue: 0,0:29:45.32,0:29:48.21,Default,,0,0,0,,这是求值第一个参数所需要的
Dialogue: 0,0:29:49.04,0:29:51.18,Default,,0,0,0,,因此 它需要保证
Dialogue: 0,0:29:51.92,0:29:53.76,Default,,0,0,0,,对运算对象的编译
Dialogue: 0,0:29:55.32,0:29:57.85,Default,,0,0,0,,或者说它需要保护FUN寄存器
Dialogue: 0,0:29:58.01,0:30:00.98,Default,,0,0,0,,来应对编译运算对象时发生的各种情况
Dialogue: 0,0:30:01.30,0:30:03.08,Default,,0,0,0,,因此它在这做了个标注说
Dialogue: 0,0:30:03.37,0:30:12.89,Default,,0,0,0,,这个片段需要保护ENV寄存器
Dialogue: 0,0:30:17.39,0:30:18.44,Default,,0,0,0,,与之类似 这里
Dialogue: 0,0:30:21.02,0:30:23.30,Default,,0,0,0,,在完成第一个运算对象的编译后
Dialogue: 0,0:30:23.57,0:30:24.67,Default,,0,0,0,,它会说 我最好--
Dialogue: 0,0:30:24.71,0:30:27.92,Default,,0,0,0,,我需要知道第二个运算对象的环境
Dialogue: 0,0:30:27.92,0:30:29.46,Default,,0,0,0,,所以它在这做了个标注
Dialogue: 0,0:30:29.71,0:30:35.96,Default,,0,0,0,,这里也需要保护ENV
Dialogue: 0,0:30:39.42,0:30:41.02,Default,,0,0,0,,现在它继续运行
Dialogue: 0,0:30:41.12,0:30:42.83,Default,,0,0,0,,下一段代码
Dialogue: 0,0:30:43.31,0:30:49.74,Default,,0,0,0,,是要编译第二个参数
Dialogue: 0,0:30:50.82,0:30:52.64,Default,,0,0,0,,它将会
Dialogue: 0,0:30:52.99,0:30:59.28,Default,,0,0,0,,把编译的结果按约定放入到VAL中
Dialogue: 0,0:31:03.86,0:31:06.70,Default,,0,0,0,,随后它会生成一条指令
Dialogue: 0,0:31:07.84,0:31:09.25,Default,,0,0,0,,从而建立起参数表
Dialogue: 0,0:31:09.55,0:31:15.28,Default,,0,0,0,,(ASSIGN ARGL
Dialogue: 0,0:31:20.22,0:31:28.94,Default,,0,0,0,,(CONS (FETCH VAL) (FETCH ARGL))
Dialogue: 0,0:31:33.97,0:31:34.64,Default,,0,0,0,,然而
Dialogue: 0,0:31:34.81,0:31:36.58,Default,,0,0,0,,为了取得旧的参数表
Dialogue: 0,0:31:37.15,0:31:40.99,Default,,0,0,0,,它最好保证这期间发生的任何事情
Dialogue: 0,0:31:41.30,0:31:42.69,Default,,0,0,0,,都不影响旧的参数表
Dialogue: 0,0:31:43.50,0:31:45.17,Default,,0,0,0,,因此它在这做了个标注说
Dialogue: 0,0:31:45.34,0:31:51.64,Default,,0,0,0,,哦 这里需要保护ARGL
Dialogue: 0,0:31:54.16,0:31:56.03,Default,,0,0,0,,现在参数表就建立好了
Dialogue: 0,0:31:58.01,0:32:02.86,Default,,0,0,0,,现在可以准备去APPLY-DISPATCH了
Dialogue: 0,0:32:07.02,0:32:10.80,Default,,0,0,0,,它生成了这条指令
Dialogue: 0,0:32:15.19,0:32:17.37,Default,,0,0,0,,因为现在参数都在ARGL中
Dialogue: 0,0:32:18.15,0:32:20.59,Default,,0,0,0,,运算符在FUN中
Dialogue: 0,0:32:20.59,0:32:22.89,Default,,0,0,0,,如果只是单纯地把运算符放到FUN寄存器中
Dialogue: 0,0:32:23.27,0:32:26.64,Default,,0,0,0,,就需要它保证这块代码
Dialogue: 0,0:32:27.09,0:32:29.27,Default,,0,0,0,,不会破坏FUN寄存器里的东西
Dialogue: 0,0:32:29.67,0:32:31.24,Default,,0,0,0,,所以它在这做了个标注说
Dialogue: 0,0:32:31.55,0:32:32.73,Default,,0,0,0,,这里的所有东西
Dialogue: 0,0:32:34.88,0:32:40.73,Default,,0,0,0,,最好能够在保护FUN寄存器的情况下完成
Dialogue: 0,0:32:43.71,0:32:46.15,Default,,0,0,0,,所以这就是--
Dialogue: 0,0:32:46.15,0:32:47.10,Default,,0,0,0,,基本上来说
Dialogue: 0,0:32:48.20,0:32:50.24,Default,,0,0,0,,编译器所做的就是
Dialogue: 0,0:32:50.54,0:32:52.46,Default,,0,0,0,,追加一大堆的代码
Dialogue: 0,0:32:53.50,0:32:58.83,Default,,0,0,0,,而这些代码之中都是一些基本运算
Dialogue: 0,0:32:58.86,0:33:00.12,Default,,0,0,0,,比如符号查找
Dialogue: 0,0:33:01.44,0:33:02.60,Default,,0,0,0,,条件分支的处理
Dialogue: 0,0:33:02.64,0:33:05.44,Default,,0,0,0,,都是一些琐碎的事情
Dialogue: 0,0:33:05.44,0:33:07.99,Default,,0,0,0,,然后按照这种准则将它们追加到一起
Dialogue: 0,0:33:08.78,0:33:10.79,Default,,0,0,0,,因此 组合的基本手段就是
Dialogue: 0,0:33:10.86,0:33:13.18,Default,,0,0,0,,将一段代码追加到另一段的后面
Dialogue: 0,0:33:21.55,0:33:22.86,Default,,0,0,0,,就是这里发生的事情
Dialogue: 0,0:33:25.58,0:33:27.24,Default,,0,0,0,,这有点取巧
Dialogue: 0,0:33:27.56,0:33:30.37,Default,,0,0,0,,向一段代码后面追加代码的思路是
Dialogue: 0,0:33:31.60,0:33:33.76,Default,,0,0,0,,小心保护寄存器
Dialogue: 0,0:33:35.63,0:33:37.93,Default,,0,0,0,,追加操作看起来像这样
Dialogue: 0,0:33:39.15,0:33:40.65,Default,,0,0,0,,它要做的是
Dialogue: 0,0:33:41.20,0:33:44.11,Default,,0,0,0,,代码的追加是这么来做的
Dialogue: 0,0:33:44.53,0:33:53.63,Default,,0,0,0,,如果SEQ1需要寄存器--
Dialogue: 0,0:33:53.66,0:33:54.72,Default,,0,0,0,,我应该改一下这个
Dialogue: 0,0:33:54.72,0:33:56.87,Default,,0,0,0,,在SEQ1后面追加SEQ2
Dialogue: 0,0:33:57.42,0:34:03.96,Default,,0,0,0,,并保护一些寄存器
Dialogue: 0,0:34:08.52,0:34:09.91,Default,,0,0,0,,这里改成AND
Dialogue: 0,0:34:11.36,0:34:13.03,Default,,0,0,0,,这样的话前后顺序就清楚了
Dialogue: 0,0:34:13.88,0:34:19.87,Default,,0,0,0,,如果SEQ2需要寄存器
Dialogue: 0,0:34:21.12,0:34:27.85,Default,,0,0,0,,而SEQ1又修改了寄存器
Dialogue: 0,0:34:33.68,0:34:36.30,Default,,0,0,0,,那么编译器生成的指令是
Dialogue: 0,0:34:36.97,0:34:41.34,Default,,0,0,0,,保存寄存器
Dialogue: 0,0:34:43.02,0:34:44.19,Default,,0,0,0,,这就是代码
Dialogue: 0,0:34:44.35,0:34:45.35,Default,,0,0,0,,生成这段代码
Dialogue: 0,0:34:45.35,0:34:46.28,Default,,0,0,0,,保存寄存器
Dialogue: 0,0:34:46.72,0:34:52.97,Default,,0,0,0,,然后写下递归编译SEQ1的结果
Dialogue: 0,0:34:53.30,0:34:54.84,Default,,0,0,0,,然后恢复寄存器
Dialogue: 0,0:35:00.52,0:35:03.92,Default,,0,0,0,,再写下递归编译
Dialogue: 0,0:35:04.46,0:35:05.47,Default,,0,0,0,,SEQ2的结果
Dialogue: 0,0:35:07.07,0:35:09.62,Default,,0,0,0,,这就是你需要做的
Dialogue: 0,0:35:09.62,0:35:11.82,Default,,0,0,0,,实际上SEQ2需要寄存器
Dialogue: 0,0:35:11.82,0:35:13.74,Default,,0,0,0,,而SEQ1改动了它
Dialogue: 0,0:35:15.12,0:35:17.07,Default,,0,0,0,,否则的话
Dialogue: 0,0:35:20.50,0:35:26.57,Default,,0,0,0,,得到的就是SEQ1后面跟着SEQ2
Dialogue: 0,0:35:28.17,0:35:30.30,Default,,0,0,0,,这就是把两个代码片段
Dialogue: 0,0:35:30.59,0:35:33.52,Default,,0,0,0,,连接到一起的基本操作
Dialogue: 0,0:35:33.93,0:35:35.93,Default,,0,0,0,,把这些指令组合成序列
Dialogue: 0,0:35:36.89,0:35:38.87,Default,,0,0,0,,我们可以发现 从这个角度看
Dialogue: 0,0:35:40.94,0:35:45.96,Default,,0,0,0,,解释器和编译器的区别
Dialogue: 0,0:35:46.82,0:35:49.34,Default,,0,0,0,,是编译器有保护寄存器的标注
Dialogue: 0,0:35:50.14,0:35:52.22,Default,,0,0,0,,上面记录着
Dialogue: 0,0:35:52.49,0:35:54.22,Default,,0,0,0,,是否需要生成保存-恢复代码
Dialogue: 0,0:35:55.19,0:35:57.24,Default,,0,0,0,,而解释器会以最悲观的方式处理
Dialogue: 0,0:35:57.28,0:35:58.90,Default,,0,0,0,,总是会进行保存-恢复
Dialogue: 0,0:36:00.76,0:36:01.93,Default,,0,0,0,,这就是关键的区别
Dialogue: 0,0:36:04.16,0:36:06.05,Default,,0,0,0,,为了实现这个
Dialogue: 0,0:36:06.65,0:36:09.40,Default,,0,0,0,,编译器需要一些理论
Dialogue: 0,0:36:09.56,0:36:11.96,Default,,0,0,0,,来确定代码序列会需要、又会修改哪些寄存器
Dialogue: 0,0:36:14.26,0:36:17.28,Default,,0,0,0,,所以你放入的小片段
Dialogue: 0,0:36:17.48,0:36:21.00,Default,,0,0,0,,例如这段基础代码
Dialogue: 0,0:36:22.74,0:36:24.59,Default,,0,0,0,,当你查找一个变量时
Dialogue: 0,0:36:24.92,0:36:26.04,Default,,0,0,0,,进行了哪些操作?
Dialogue: 0,0:36:26.89,0:36:29.02,Default,,0,0,0,,你又是做了些什么
Dialogue: 0,0:36:29.05,0:36:30.68,Default,,0,0,0,,来编译一个常量
Dialogue: 0,0:36:30.97,0:36:32.10,Default,,0,0,0,,或者应用一个函数
Dialogue: 0,0:36:32.97,0:36:34.48,Default,,0,0,0,,它们都会带有一些标注
Dialogue: 0,0:36:34.67,0:36:36.46,Default,,0,0,0,,说明了它们需要的和修改的寄存器
Dialogue: 0,0:36:38.78,0:36:41.50,Default,,0,0,0,,所以底层的数据结构
Dialogue: 0,0:36:42.66,0:36:44.33,Default,,0,0,0,,我会这样讲
Dialogue: 0,0:36:44.39,0:36:47.91,Default,,0,0,0,,传递给编译器的代码序列大概是这样
Dialogue: 0,0:36:48.07,0:36:51.42,Default,,0,0,0,,它里面有实际的指令序列
Dialogue: 0,0:36:55.67,0:36:56.81,Default,,0,0,0,,跟它一起的还有
Dialogue: 0,0:36:57.18,0:37:02.60,Default,,0,0,0,,一组被修改的寄存器
Dialogue: 0,0:37:10.54,0:37:12.60,Default,,0,0,0,,还有一组需要的寄存器
Dialogue: 0,0:37:20.00,0:37:22.46,Default,,0,0,0,,为了能够执行此操作
Dialogue: 0,0:37:23.00,0:37:26.41,Default,,0,0,0,,编译器必须要掌握这些信息
Dialogue: 0,0:37:29.30,0:37:31.08,Default,,0,0,0,,它们从哪来呢
Dialogue: 0,0:37:32.91,0:37:34.49,Default,,0,0,0,,它们来自于--你们可能也想到了
Dialogue: 0,0:37:34.51,0:37:35.53,Default,,0,0,0,,对于那些最基本的片段
Dialogue: 0,0:37:35.55,0:37:36.84,Default,,0,0,0,,我们会手工添加
Dialogue: 0,0:37:37.24,0:37:38.86,Default,,0,0,0,,然后 当我们组合两个序列时
Dialogue: 0,0:37:38.89,0:37:41.02,Default,,0,0,0,,我们会计算出这两个集合
Dialogue: 0,0:37:42.16,0:37:44.12,Default,,0,0,0,,举一个非常基本的例子
Dialogue: 0,0:37:48.43,0:37:51.40,Default,,0,0,0,,例如做一个寄存器赋值
Dialogue: 0,0:37:51.77,0:37:53.50,Default,,0,0,0,,因此 基本代码片段会说
Dialogue: 0,0:37:53.52,0:37:56.22,Default,,0,0,0,,噢 它是个代码片段
Dialogue: 0,0:37:56.22,0:38:03.17,Default,,0,0,0,,代码的指令部分是(ASSIGN R1 (FETCH R2))
Dialogue: 0,0:38:03.17,0:38:04.27,Default,,0,0,0,,这个例子就是这样的
Dialogue: 0,0:38:05.42,0:38:08.52,Default,,0,0,0,,一段指令序列大概就是这样
Dialogue: 0,0:38:08.77,0:38:10.53,Default,,0,0,0,,和它在一起的是
Dialogue: 0,0:38:10.64,0:38:15.76,Default,,0,0,0,,它需要记得修改了R1
Dialogue: 0,0:38:18.60,0:38:21.16,Default,,0,0,0,,然后它需要R2
Dialogue: 0,0:38:24.69,0:38:26.99,Default,,0,0,0,,因此当你开始构建编译器时
Dialogue: 0,0:38:27.10,0:38:29.35,Default,,0,0,0,,你放入这样的一个片段
Dialogue: 0,0:38:30.95,0:38:33.20,Default,,0,0,0,,当它组合两个序列时
Dialogue: 0,0:38:36.70,0:38:38.04,Default,,0,0,0,,我要组合
Dialogue: 0,0:38:38.92,0:38:41.58,Default,,0,0,0,,代码片段S1
Dialogue: 0,0:38:42.88,0:38:47.16,Default,,0,0,0,,修改了一组寄存器M1
Dialogue: 0,0:38:48.45,0:38:51.42,Default,,0,0,0,,并且需要一组寄存器N1
Dialogue: 0,0:38:54.85,0:38:59.48,Default,,0,0,0,,并且我要把它和序列S2组合到一起
Dialogue: 0,0:39:00.81,0:39:05.96,Default,,0,0,0,,后者修改了一组寄存器M2
Dialogue: 0,0:39:07.11,0:39:10.00,Default,,0,0,0,,并且需要一组寄存器N2
Dialogue: 0,0:39:12.44,0:39:14.83,Default,,0,0,0,,这样我们就能得出结果
Dialogue: 0,0:39:15.11,0:39:16.32,Default,,0,0,0,,新的代码片段是这样的
Dialogue: 0,0:39:17.18,0:39:21.82,Default,,0,0,0,,指令序列S1后面跟着S2
Dialogue: 0,0:39:24.09,0:39:26.45,Default,,0,0,0,,它要修改什么?
Dialogue: 0,0:39:27.80,0:39:29.18,Default,,0,0,0,,它要修改的是
Dialogue: 0,0:39:29.20,0:39:32.68,Default,,0,0,0,,被S1或者被S2修改的寄存器
Dialogue: 0,0:39:34.00,0:39:36.35,Default,,0,0,0,,N1和N2的并集
Dialogue: 0,0:39:37.68,0:39:39.64,Default,,0,0,0,,就是新的修改集
Dialogue: 0,0:39:40.46,0:39:41.79,Default,,0,0,0,,然后你问
Dialogue: 0,0:39:44.66,0:39:46.41,Default,,0,0,0,,又需要哪些寄存器?
Dialogue: 0,0:39:47.95,0:39:49.77,Default,,0,0,0,,需要这些寄存器的是
Dialogue: 0,0:39:49.93,0:39:51.85,Default,,0,0,0,,首先 一定是序列S1需要的
Dialogue: 0,0:39:52.91,0:39:54.49,Default,,0,0,0,,因此必然有N1
Dialogue: 0,0:39:55.19,0:39:58.28,Default,,0,0,0,,然后 并不是N2里面的所有元素
Dialogue: 0,0:39:58.32,0:39:59.61,Default,,0,0,0,,我们都需要
Dialogue: 0,0:39:59.75,0:40:03.49,Default,,0,0,0,,新的修改集需要N2中那些
Dialogue: 0,0:40:03.88,0:40:06.88,Default,,0,0,0,,没有被S1修改过的寄存器
Dialogue: 0,0:40:08.14,0:40:09.72,Default,,0,0,0,,所以 这个并集是N1并上
Dialogue: 0,0:40:11.66,0:40:13.40,Default,,0,0,0,,序列S2的需要集N2
Dialogue: 0,0:40:14.51,0:40:18.52,Default,,0,0,0,,减去序列S1的修改集M1
Dialogue: 0,0:40:19.31,0:40:20.88,Default,,0,0,0,,因为它关心的是如何设置它们
Dialogue: 0,0:40:23.95,0:40:26.26,Default,,0,0,0,,这就是编译器的基本结构
Dialogue: 0,0:40:26.70,0:40:29.82,Default,,0,0,0,,我们进行寄存器优化的方式是
Dialogue: 0,0:40:30.22,0:40:32.70,Default,,0,0,0,,一些策略来应对需要保护的东西
Dialogue: 0,0:40:34.10,0:40:35.63,Default,,0,0,0,,这取决于数据结构
Dialogue: 0,0:40:35.72,0:40:38.51,Default,,0,0,0,,这取决于将东西组合在一起的操作
Dialogue: 0,0:40:39.03,0:40:41.63,Default,,0,0,0,,想知道要保护哪些东西
Dialogue: 0,0:40:41.93,0:40:47.28,Default,,0,0,0,,就需要知道这段代码需要以及修改的寄存器
Dialogue: 0,0:40:48.75,0:40:51.26,Default,,0,0,0,,这就需要我们有一个数据结构
Dialogue: 0,0:40:51.42,0:40:55.43,Default,,0,0,0,,它不但要存放实际的指令序列
Dialogue: 0,0:40:55.60,0:40:57.33,Default,,0,0,0,,它修改了什么 又需要什么
Dialogue: 0,0:40:57.33,0:40:59.77,Default,,0,0,0,,这些信息来自于--最基本的情况是内置的
Dialogue: 0,0:40:59.79,0:41:01.36,Default,,0,0,0,,对于最基本的情况
Dialogue: 0,0:41:01.37,0:41:02.52,Default,,0,0,0,,我们可以容易地知道
Dialogue: 0,0:41:03.00,0:41:04.44,Default,,0,0,0,,需要哪些寄存器 又修改了哪些
Dialogue: 0,0:41:04.82,0:41:05.35,Default,,0,0,0,,另外
Dialogue: 0,0:41:05.44,0:41:08.60,Default,,0,0,0,,利用这个特定的方法构建复杂指令时
Dialogue: 0,0:41:09.28,0:41:11.89,Default,,0,0,0,,我们可以像这样生成新的修改集
Dialogue: 0,0:41:11.93,0:41:13.37,Default,,0,0,0,,以及新的需要集
Dialogue: 0,0:41:15.27,0:41:17.77,Default,,0,0,0,,这就是全部的内容 -- 我不该这么说
Dialogue: 0,0:41:17.77,0:41:19.34,Default,,0,0,0,,这就是书里面大概30页的细节
Dialogue: 0,0:41:19.74,0:41:21.87,Default,,0,0,0,,的核心内容了
Dialogue: 0,0:41:22.31,0:41:27.69,Default,,0,0,0,,但它是一个完全可用的初级编译器
Dialogue: 0,0:41:28.76,0:41:31.37,Default,,0,0,0,,让我给你展示一下它能做什么
Dialogue: 0,0:41:31.39,0:41:35.56,Default,,0,0,0,,假设我们从一个递归阶乘开始
Dialogue: 0,0:41:36.20,0:41:38.60,Default,,0,0,0,,这些幻灯片的字太小不适合阅读
Dialogue: 0,0:41:38.60,0:41:39.79,Default,,0,0,0,,我只想快速翻一下代码
Dialogue: 0,0:41:39.79,0:41:41.28,Default,,0,0,0,,让你们看看它有多少代码
Dialogue: 0,0:41:42.25,0:41:43.29,Default,,0,0,0,,代码从这开始--
Dialogue: 0,0:41:44.32,0:41:45.68,Default,,0,0,0,,这是代码的第一部分
Dialogue: 0,0:41:45.95,0:41:47.68,Default,,0,0,0,,这里编译了一个过程入口
Dialogue: 0,0:41:47.69,0:41:48.73,Default,,0,0,0,,并进行了一些赋值操作
Dialogue: 0,0:41:48.75,0:41:51.48,Default,,0,0,0,,这基本上对应了解释器中
Dialogue: 0,0:41:52.65,0:41:53.90,Default,,0,0,0,,进行判断之前的部分
Dialogue: 0,0:41:54.31,0:41:56.59,Default,,0,0,0,,并判断谓词是否成立
Dialogue: 0,0:41:56.97,0:41:57.85,Default,,0,0,0,,第二部分是
Dialogue: 0,0:41:58.46,0:42:03.73,Default,,0,0,0,,递归调用N-1的阶乘的结果
Dialogue: 0,0:42:04.12,0:42:05.05,Default,,0,0,0,,最后一部分是
Dialogue: 0,0:42:06.07,0:42:07.48,Default,,0,0,0,,从那里返回
Dialogue: 0,0:42:07.87,0:42:09.90,Default,,0,0,0,,并处理递归的基本情况
Dialogue: 0,0:42:09.90,0:42:13.16,Default,,0,0,0,,这就是编译阶乘会生成的代码量
Dialogue: 0,0:42:13.72,0:42:17.69,Default,,0,0,0,,当然 我们可以把这个编译器做得更好
Dialogue: 0,0:42:18.67,0:42:21.24,Default,,0,0,0,,优化它的主要方式是
Dialogue: 0,0:42:21.24,0:42:24.00,Default,,0,0,0,,当你调用一个过程时
Dialogue: 0,0:42:24.35,0:42:26.27,Default,,0,0,0,,允许编译器做任何假设
Dialogue: 0,0:42:26.97,0:42:28.28,Default,,0,0,0,,举例来说
Dialogue: 0,0:42:28.30,0:42:32.32,Default,,0,0,0,,这个编译器甚至不知道
Dialogue: 0,0:42:33.12,0:42:36.14,Default,,0,0,0,,乘法可以被内联执行
Dialogue: 0,0:42:36.14,0:42:37.87,Default,,0,0,0,,它则会自行构建起整个机制
Dialogue: 0,0:42:38.00,0:42:39.34,Default,,0,0,0,,进行APPLY-DISPATCH
Dialogue: 0,0:42:41.37,0:42:42.49,Default,,0,0,0,,这是极大的浪费
Dialogue: 0,0:42:42.54,0:42:45.02,Default,,0,0,0,,因为 每当你进行APPLY-DISPATCH时
Dialogue: 0,0:42:45.02,0:42:46.80,Default,,0,0,0,,你都要关心这个参数表
Dialogue: 0,0:42:47.40,0:42:49.10,Default,,0,0,0,,因为它是个很普遍的操作
Dialogue: 0,0:42:49.13,0:42:51.07,Default,,0,0,0,,在任何真实的编译器中
Dialogue: 0,0:42:51.08,0:42:53.29,Default,,0,0,0,,你会有寄存器来暂存参数
Dialogue: 0,0:42:53.77,0:42:55.31,Default,,0,0,0,,你要开始保护
Dialogue: 0,0:42:56.38,0:42:58.05,Default,,0,0,0,,保存这些寄存器
Dialogue: 0,0:42:58.05,0:43:01.61,Default,,0,0,0,,和这里的策略相近
Dialogue: 0,0:43:02.85,0:43:05.93,Default,,0,0,0,,因此 我们可能主要通过这个方法
Dialogue: 0,0:43:05.95,0:43:08.30,Default,,0,0,0,,来优化书中这个特定的编译器
Dialogue: 0,0:43:08.69,0:43:09.70,Default,,0,0,0,,还有其它的一些方法
Dialogue: 0,0:43:09.70,0:43:11.82,Default,,0,0,0,,比如查找变量的值
Dialogue: 0,0:43:11.83,0:43:13.87,Default,,0,0,0,,使用更高效的基本操作
Dialogue: 0,0:43:13.88,0:43:14.56,Default,,0,0,0,,等等方法
Dialogue: 0,0:43:14.59,0:43:16.60,Default,,0,0,0,,本质上来说 一个好的Lisp编译器
Dialogue: 0,0:43:16.62,0:43:18.49,Default,,0,0,0,,可以吸收任意数量的努力
Dialogue: 0,0:43:19.72,0:43:21.63,Default,,0,0,0,,可能这其中的一个原因是
Dialogue: 0,0:43:21.89,0:43:23.04,Default,,0,0,0,,跟FORTRAN这类语言相比
Dialogue: 0,0:43:23.63,0:43:25.44,Default,,0,0,0,,Lisp就要慢一些
Dialogue: 0,0:43:25.90,0:43:28.19,Default,,0,0,0,,如果你回头审视历史
Dialogue: 0,0:43:28.22,0:43:31.12,Default,,0,0,0,,会发现人们为构建Lisp编译器而呕心沥血
Dialogue: 0,0:43:31.16,0:43:32.35,Default,,0,0,0,,但也远远没有接近
Dialogue: 0,0:43:32.36,0:43:33.90,Default,,0,0,0,,构建FORTRAN编译器的工作量
Dialogue: 0,0:43:34.43,0:43:35.79,Default,,0,0,0,,在接下来的几年
Dialogue: 0,0:43:35.92,0:43:37.68,Default,,0,0,0,,情况可能会发生变化
Dialogue: 0,0:43:38.00,0:43:38.83,Default,,0,0,0,,好吧 就讲到这里
Dialogue: 0,0:43:43.80,0:43:44.65,Default,,0,0,0,,有问题吗
Dialogue: 0,0:43:48.27,0:43:49.95,Default,,0,0,0,,学生: 很早的一个课时里--
Dialogue: 0,0:43:49.95,0:43:51.40,Default,,0,0,0,,我不记得是课上还是课后--
Dialogue: 0,0:43:51.47,0:43:53.88,Default,,0,0,0,,你向我们展示了
Dialogue: 0,0:43:54.00,0:43:57.52,Default,,0,0,0,,ADD操作有一些我们看不到的基本运算
Dialogue: 0,0:43:57.69,0:43:59.21,Default,,0,0,0,,类似于ADD%之类的
Dialogue: 0,0:43:59.82,0:44:01.65,Default,,0,0,0,,这是因为
Dialogue: 0,0:44:01.65,0:44:02.60,Default,,0,0,0,,你想把代码内联为
Dialogue: 0,0:44:02.60,0:44:08.19,Default,,0,0,0,,专门针对二元运算对象的运算么?
Dialogue: 0,0:44:08.70,0:44:10.25,Default,,0,0,0,,但如果你有更多的运算对象
Dialogue: 0,0:44:10.28,0:44:11.47,Default,,0,0,0,,你会做什么特殊的事情吗?
Dialogue: 0,0:44:12.71,0:44:16.04,Default,,0,0,0,,教授: 你看的是Scheme的实际实现
Dialogue: 0,0:44:16.06,0:44:17.84,Default,,0,0,0,,其中有一个‘+’ 这是一个运算符
Dialogue: 0,0:44:17.90,0:44:20.19,Default,,0,0,0,,如果你看‘+’的源代码
Dialogue: 0,0:44:20.33,0:44:21.37,Default,,0,0,0,,你会看到一些叫做--
Dialogue: 0,0:44:21.57,0:44:24.14,Default,,0,0,0,,我记不清了--可能叫ADD%、PLUS之类的东西
Dialogue: 0,0:44:24.55,0:44:25.79,Default,,0,0,0,,这里所进行的
Dialogue: 0,0:44:25.79,0:44:27.92,Default,,0,0,0,,就是你说的那种优化
Dialogue: 0,0:44:28.47,0:44:31.87,Default,,0,0,0,,因为 广义的‘+’接受任意数量的参数
Dialogue: 0,0:44:35.02,0:44:36.38,Default,,0,0,0,,所以 广义加法
Dialogue: 0,0:44:36.76,0:44:38.25,Default,,0,0,0,,会说:如果我有一个参数表
Dialogue: 0,0:44:38.28,0:44:40.62,Default,,0,0,0,,我最好将它们用CONS连接到表里
Dialogue: 0,0:44:41.63,0:44:44.14,Default,,0,0,0,,并指出有多少个参数
Dialogue: 0,0:44:44.72,0:44:46.16,Default,,0,0,0,,这样效率非常低下
Dialogue: 0,0:44:46.81,0:44:49.25,Default,,0,0,0,,因为大部分时间你在把两个数相加
Dialogue: 0,0:44:49.25,0:44:51.24,Default,,0,0,0,,你不必把整个参数表连接到一起
Dialogue: 0,0:44:52.04,0:44:53.93,Default,,0,0,0,,所以你想做的是
Dialogue: 0,0:44:55.66,0:44:57.71,Default,,0,0,0,,构建把一堆东西相加的代码
Dialogue: 0,0:44:58.15,0:45:00.17,Default,,0,0,0,,所以它做的大部分事情是一样的
Dialogue: 0,0:45:00.49,0:45:01.95,Default,,0,0,0,,但这里可能有个特殊的入口
Dialogue: 0,0:45:01.98,0:45:03.92,Default,,0,0,0,,如果你知道只有两个参数
Dialogue: 0,0:45:04.56,0:45:05.87,Default,,0,0,0,,你会把它们放到寄存器中
Dialogue: 0,0:45:05.87,0:45:06.97,Default,,0,0,0,,它们不需要参数表
Dialogue: 0,0:45:06.99,0:45:07.98,Default,,0,0,0,,你也不必用CONS连接它们
Dialogue: 0,0:45:08.67,0:45:10.42,Default,,0,0,0,,这就是这些东西工作的原理
Dialogue: 0,0:45:12.30,0:45:13.72,Default,,0,0,0,,好吧 下课吧
Dialogue: 0,0:45:14.10,0:45:42.97,Declare,,0,0,0,,{\fad(500,500)}MIT OpenCourseWare\Nhttp://ocw.mit.edu
Dialogue: 0,0:45:14.10,0:45:42.97,Declare,,0,0,0,,{\an2\fad(500,500)}本项目主页\Nhttps://github.com/DeathKing/Learning-SICP


================================================
FILE: Ass/lec10a.eng.ass
================================================
[Script Info]
; Script generated by Aegisub 3.2.2
; http://www.aegisub.org/
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.601
PlayResX: 640
PlayResY: 480

[Aegisub Project Garbage]

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: EN,Calisto MT,21,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,1,0,2,10,10,30,1
Style: Declare,微软雅黑,30,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,2,0,8,10,10,10,1
Style: staff,微软雅黑,30,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,0,2,5,10,10,10,1
Style: title,微软雅黑,35,&H001D64D9,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,0,1,5,10,10,10,1
Style: Default,雅黑宋体,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,1,0,2,10,10,30,1


[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:11.84,0:00:13.84,EN,,0,0,0,,Compilation
Dialogue: 0,0:00:19.36,0:00:22.65,EN,,0,0,0,,PROFESSOR: Last time, we took a look at
Dialogue: 0,0:00:22.65,0:00:25.67,EN,,0,0,0,,an explicit control evaluator for Lisp
Dialogue: 0,0:00:25.67,0:00:28.97,EN,,0,0,0,,and that bridged the gap between all these high-level languages
Dialogue: 0,0:00:29.05,0:00:32.14,EN,,0,0,0,,like Lisp and query language all that stuff
Dialogue: 0,0:00:32.50,0:00:36.16,EN,,0,0,0,,bridged the gap between that and a conventional register machine.
Dialogue: 0,0:00:36.70,0:00:40.14,EN,,0,0,0,,And in fact, you can think of the explicit control evaluator
Dialogue: 0,0:00:40.16,0:00:44.38,EN,,0,0,0,,either as, say the code for a Lisp interpreter
Dialogue: 0,0:00:44.40,0:00:45.95,EN,,0,0,0,,if you wanted to implement it in the
Dialogue: 0,0:00:46.52,0:00:49.50,EN,,0,0,0,,assembly language of some conventional register transfer machine,
Dialogue: 0,0:00:49.50,0:00:51.50,EN,,0,0,0,,or, if you like, you can think of it as the microcode
Dialogue: 0,0:00:52.08,0:00:54.56,EN,,0,0,0,,of some machine that's going to be specially designed to run Lisp.
Dialogue: 0,0:00:55.20,0:00:55.92,EN,,0,0,0,,In either case,
Dialogue: 0,0:00:55.92,0:00:58.68,EN,,0,0,0,,Nwhat we're doing is we're taking a machine
Dialogue: 0,0:00:58.94,0:01:00.51,EN,,0,0,0,,that speaks some low-level language
Dialogue: 0,0:01:01.42,0:01:03.32,EN,,0,0,0,,and we're raising the machine
Dialogue: 0,0:01:03.37,0:01:04.88,EN,,0,0,0,,to a high-level language like Lisp
Dialogue: 0,0:01:05.36,0:01:06.35,EN,,0,0,0,,by writing an interpreter.
Dialogue: 0,0:01:08.22,0:01:09.58,EN,,0,0,0,,So for instance
Dialogue: 0,0:01:11.82,0:01:13.77,EN,,0,0,0,,here, conceptually,
Dialogue: 0,0:01:18.01,0:01:19.47,EN,,0,0,0,,here conceptually is a
Dialogue: 0,0:01:20.54,0:01:23.44,EN,,0,0,0,,a special purpose machine for computing factorials.
Dialogue: 0,0:01:24.09,0:01:27.39,EN,,0,0,0,,It takes in five and puts out 120.
Dialogue: 0,0:01:28.92,0:01:30.83,EN,,0,0,0,,And what this special purpose machine is
Dialogue: 0,0:01:30.97,0:01:32.72,EN,,0,0,0,,actually a Lisp interpreter
Dialogue: 0,0:01:33.50,0:01:36.17,EN,,0,0,0,,that's configured itself to run factorials
Dialogue: 0,0:01:38.35,0:01:40.99,EN,,0,0,0,,because you feed into it a description of the factorial machine.
Dialogue: 0,0:01:42.12,0:01:43.70,EN,,0,0,0,,So that's what an interpreter is.
Dialogue: 0,0:01:43.70,0:01:45.66,EN,,0,0,0,,It configures itself to
Dialogue: 0,0:01:46.37,0:01:49.24,EN,,0,0,0,,emulate a machine whose description you read in.
Dialogue: 0,0:01:50.07,0:01:51.93,EN,,0,0,0,,Now, inside the Lisp interpreter, what's that?
Dialogue: 0,0:01:52.04,0:01:55.44,EN,,0,0,0,,Well, that might be your general register language interpreter
Dialogue: 0,0:01:56.98,0:02:00.18,EN,,0,0,0,,that configures itself to behave like a Lisp interpreter
Dialogue: 0,0:02:00.18,0:02:02.03,EN,,0,0,0,,because you put in a whole bunch of instructions
Dialogue: 0,0:02:02.12,0:02:03.04,EN,,0,0,0,,in register language.
Dialogue: 0,0:02:03.37,0:02:05.16,EN,,0,0,0,,This is the explicit control evaluator.
Dialogue: 0,0:02:07.05,0:02:08.70,EN,,0,0,0,,And then it also has some sort of library
Dialogue: 0,0:02:08.73,0:02:11.08,EN,,0,0,0,,a library of primitive operators and Lisp operations
Dialogue: 0,0:02:11.12,0:02:12.28,EN,,0,0,0,,all sorts of things like that.
Dialogue: 0,0:02:12.75,0:02:16.89,EN,,0,0,0,,That's the general strategy of interpretation.
Dialogue: 0,0:02:17.32,0:02:18.51,EN,,0,0,0,,And the point is, what we're doing
Dialogue: 0,0:02:18.60,0:02:20.14,EN,,0,0,0,,is we're writing an interpreter
Dialogue: 0,0:02:21.62,0:02:23.40,EN,,0,0,0,,to raise the machine
Dialogue: 0,0:02:23.42,0:02:25.24,EN,,0,0,0,,to the level of the programs that we want to write.
Dialogue: 0,0:02:25.24,0:02:26.72,EN,,0,0,0,,Well, there's another strategy
Dialogue: 0,0:02:27.42,0:02:28.89,EN,,0,0,0,,a different one, which is compilation.
Dialogue: 0,0:02:29.04,0:02:30.43,EN,,0,0,0,,Compilation's a little bit different.
Dialogue: 0,0:02:31.04,0:02:31.50,EN,,0,0,0,,Here--
Dialogue: 0,0:02:33.37,0:02:34.75,EN,,0,0,0,,here we might have produced
Dialogue: 0,0:02:35.67,0:02:38.52,EN,,0,0,0,,a special purpose machine for,
Dialogue: 0,0:02:38.62,0:02:39.98,EN,,0,0,0,,for computing factorials
Dialogue: 0,0:02:43.62,0:02:46.26,EN,,0,0,0,,starting with some sort of machine that speaks register language
Dialogue: 0,0:02:46.26,0:02:47.72,EN,,0,0,0,,except we're going to do a different strategy.
Dialogue: 0,0:02:47.72,0:02:50.38,EN,,0,0,0,,We take our factorial program.
Dialogue: 0,0:02:51.55,0:02:53.92,EN,,0,0,0,,We use that as the source code into a compiler.
Dialogue: 0,0:02:53.92,0:02:55.15,EN,,0,0,0,,What the compiler will do
Dialogue: 0,0:02:55.15,0:02:57.62,EN,,0,0,0,,is translate that factorial program
Dialogue: 0,0:02:57.62,0:02:59.07,EN,,0,0,0,,into some register machine language.
Dialogue: 0,0:03:00.25,0:03:03.40,EN,,0,0,0,,And this will now be not the explicit control evaluator for Lisp
Dialogue: 0,0:03:03.40,0:03:06.17,EN,,0,0,0,,this will be some register language for computing factorials.
Dialogue: 0,0:03:06.49,0:03:08.36,EN,,0,0,0,,So this is the translation of that.
Dialogue: 0,0:03:10.54,0:03:12.41,EN,,0,0,0,,That will go into some sort of loader
Dialogue: 0,0:03:13.35,0:03:15.21,EN,,0,0,0,,which will combine this code
Dialogue: 0,0:03:15.31,0:03:16.84,EN,,0,0,0,,with code selected from the library
Dialogue: 0,0:03:16.86,0:03:18.65,EN,,0,0,0,,to do things like primitive multiplication.
Dialogue: 0,0:03:19.82,0:03:21.69,EN,,0,0,0,,And then we'll produce a load module
Dialogue: 0,0:03:22.22,0:03:25.06,EN,,0,0,0,,which configures the register language machine
Dialogue: 0,0:03:25.06,0:03:27.24,EN,,0,0,0,,to be a special purpose factorial machine.
Dialogue: 0,0:03:28.12,0:03:30.22,EN,,0,0,0,,So that's a, that's a different strategy.
Dialogue: 0,0:03:30.22,0:03:31.22,EN,,0,0,0,,In interpretation,
Dialogue: 0,0:03:31.22,0:03:32.01,EN,,0,0,0,,we're raising
Dialogue: 0,0:03:32.91,0:03:35.23,EN,,0,0,0,,the machine to the level of our language, like Lisp.
Dialogue: 0,0:03:35.32,0:03:36.34,EN,,0,0,0,,In compilation
Dialogue: 0,0:03:36.34,0:03:38.43,EN,,0,0,0,,we're taking our program and lowering
Dialogue: 0,0:03:38.48,0:03:40.56,EN,,0,0,0,,it to the language that's spoken by the machine.
Dialogue: 0,0:03:41.96,0:03:43.84,EN,,0,0,0,,Well, how do these two strategies compare?
Dialogue: 0,0:03:44.30,0:03:49.42,EN,,0,0,0,,The compiler can produce code that will execute more efficiently.
Dialogue: 0,0:03:52.05,0:03:53.90,EN,,0,0,0,,The essential reason for that
Dialogue: 0,0:03:54.17,0:03:58.89,EN,,0,0,0,,is that if you think about the register operations that are running
Dialogue: 0,0:04:01.92,0:04:04.49,EN,,0,0,0,,the interpreter has to produce register operations
Dialogue: 0,0:04:04.97,0:04:06.75,EN,,0,0,0,,which, in principle, are going to be general enough
Dialogue: 0,0:04:07.32,0:04:08.94,EN,,0,0,0,,to execute any Lisp procedure.
Dialogue: 0,0:04:10.22,0:04:12.25,EN,,0,0,0,,Whereas the compiler only has to worry about
Dialogue: 0,0:04:12.27,0:04:14.92,EN,,0,0,0,,producing a special bunch of register operations for
Dialogue: 0,0:04:15.52,0:04:18.22,EN,,0,0,0,,for doing the particular Lisp procedure that you've compiled.
Dialogue: 0,0:04:20.17,0:04:21.20,EN,,0,0,0,,Or another way to say that
Dialogue: 0,0:04:21.20,0:04:25.31,EN,,0,0,0,,is that the interpreter is a general purpose simulator
Dialogue: 0,0:04:25.92,0:04:27.58,EN,,0,0,0,,that when you read in a Lisp procedure
Dialogue: 0,0:04:27.58,0:04:31.32,EN,,0,0,0,,then those can simulate the program described by that, by that procedure.
Dialogue: 0,0:04:31.32,0:04:33.87,EN,,0,0,0,,So the interpreter is worrying about making a general purpose simulator
Dialogue: 0,0:04:34.62,0:04:35.96,EN,,0,0,0,,whereas the compiler, in effect,
Dialogue: 0,0:04:36.00,0:04:37.68,EN,,0,0,0,,is configuring the thing to be the machine
Dialogue: 0,0:04:37.71,0:04:39.34,EN,,0,0,0,,that the interpreter would have been simulating.
Dialogue: 0,0:04:40.02,0:04:41.34,EN,,0,0,0,,So the compiler can be faster.
Dialogue: 0,0:04:52.55,0:04:53.64,EN,,0,0,0,,OK, On the other hand
Dialogue: 0,0:04:55.97,0:04:58.28,EN,,0,0,0,,the interpreter is a nicer environment for debugging.
Dialogue: 0,0:04:59.43,0:05:01.25,EN,,0,0,0,,And the reason for that is that we've got the
Dialogue: 0,0:05:01.57,0:05:03.02,EN,,0,0,0,,the source code actually there.
Dialogue: 0,0:05:03.02,0:05:04.81,EN,,0,0,0,,We're interpreting it That's what we're working with.
Dialogue: 0,0:05:05.87,0:05:07.69,EN,,0,0,0,,And we also have the library around.
Dialogue: 0,0:05:07.90,0:05:10.89,EN,,0,0,0,,See, the interpreter--the library sitting there is part of the interpreter.
Dialogue: 0,0:05:11.30,0:05:13.16,EN,,0,0,0,,The compiler only pulls out from the library
Dialogue: 0,0:05:13.20,0:05:14.56,EN,,0,0,0,,what it needs to run the program.
Dialogue: 0,0:05:14.87,0:05:17.00,EN,,0,0,0,,So if you're in the middle of debugging
Dialogue: 0,0:05:18.00,0:05:20.72,EN,,0,0,0,,and you might like to write a little extra program
Dialogue: 0,0:05:20.80,0:05:22.57,EN,,0,0,0,,to examine some run time data structure
Dialogue: 0,0:05:23.05,0:05:24.25,EN,,0,0,0,,or to produce some computation
Dialogue: 0,0:05:24.30,0:05:25.92,EN,,0,0,0,,that you didn't think of when you wrote the program
Dialogue: 0,0:05:25.95,0:05:27.53,EN,,0,0,0,,the interpreter can do that perfectly well
Dialogue: 0,0:05:28.05,0:05:29.21,EN,,0,0,0,,whereas the compiler can't.
Dialogue: 0,0:05:29.62,0:05:31.90,EN,,0,0,0,,So there are sort of dual, dual advantages.
Dialogue: 0,0:05:31.90,0:05:34.48,EN,,0,0,0,,The compiler will produce code that executes faster.
Dialogue: 0,0:05:34.85,0:05:37.02,EN,,0,0,0,,The interpreter is a better environment for debugging.
Dialogue: 0,0:05:38.95,0:05:41.40,EN,,0,0,0,,And most Lisp systems end up having both
Dialogue: 0,0:05:42.92,0:05:45.23,EN,,0,0,0,,end up being configured so you have an interpreter
Dialogue: 0,0:05:45.24,0:05:47.08,EN,,0,0,0,,that you use when you're developing your code.
Dialogue: 0,0:05:47.08,0:05:48.62,EN,,0,0,0,,Then you can speed it up by compiling.
Dialogue: 0,0:05:49.02,0:05:50.03,EN,,0,0,0,,And very often,
Dialogue: 0,0:05:50.04,0:05:51.68,EN,,0,0,0,,you can arrange that compiled code
Dialogue: 0,0:05:51.69,0:05:53.56,EN,,0,0,0,,and interpreted code can call each other.
Dialogue: 0,0:05:54.60,0:05:56.33,EN,,0,0,0,,We'll see how to do that, That's not hard.
Dialogue: 0,0:05:59.27,0:05:59.85,EN,,0,0,0,,OK
Dialogue: 0,0:06:00.97,0:06:02.09,EN,,0,0,0,,In fact, the way we'll--
Dialogue: 0,0:06:04.30,0:06:05.75,EN,,0,0,0,,in the compiler we're going to make
Dialogue: 0,0:06:05.75,0:06:07.58,EN,,0,0,0,,the way we'll arrange for compiled coding
Dialogue: 0,0:06:07.58,0:06:09.45,EN,,0,0,0,,and interpreted code to call to call each other
Dialogue: 0,0:06:09.90,0:06:12.06,EN,,0,0,0,,is that we'll have the compiler use exactly
Dialogue: 0,0:06:12.11,0:06:14.40,EN,,0,0,0,,the same register conventions as the interpreter.
Dialogue: 0,0:06:18.42,0:06:21.72,EN,,0,0,0,,Well, the idea of a compiler
Dialogue: 0,0:06:21.76,0:06:25.74,EN,,0,0,0,,is very much like the idea of an interpreter or evaluator.
Dialogue: 0,0:06:25.87,0:06:26.46,EN,,0,0,0,,It's the same thing.
Dialogue: 0,0:06:27.05,0:06:29.39,EN,,0,0,0,,See, the evaluator walks over the code
Dialogue: 0,0:06:29.82,0:06:32.35,EN,,0,0,0,,and performs some register operations.
Dialogue: 0,0:06:33.65,0:06:34.97,EN,,0,0,0,,That's what we did yesterday.
Dialogue: 0,0:06:37.10,0:06:40.27,EN,,0,0,0,,Well, the compiler essentially would like to walk over the code
Dialogue: 0,0:06:40.52,0:06:43.00,EN,,0,0,0,,and produce the register operations
Dialogue: 0,0:06:43.04,0:06:44.67,EN,,0,0,0,,that the evaluator would have done
Dialogue: 0,0:06:45.23,0:06:46.64,EN,,0,0,0,,were it evaluating the thing.
Dialogue: 0,0:06:48.60,0:06:49.95,EN,,0,0,0,,And that gives us some model
Dialogue: 0,0:06:50.60,0:06:53.77,EN,,0,0,0,,for how to implement a zeroth-order compiler
Dialogue: 0,0:06:55.30,0:06:58.32,EN,,0,0,0,,a very bad compiler but essentially a compiler.
Dialogue: 0,0:06:58.32,0:06:59.32,EN,,0,0,0,,A model for doing that
Dialogue: 0,0:06:59.36,0:07:00.59,EN,,0,0,0,,is you just take the evaluator,
Dialogue: 0,0:07:00.68,0:07:01.88,EN,,0,0,0,,you run it over the code
Dialogue: 0,0:07:02.80,0:07:06.06,EN,,0,0,0,,but instead of executing the actual operations
Dialogue: 0,0:07:06.06,0:07:07.15,EN,,0,0,0,,you just save them away.
Dialogue: 0,0:07:07.55,0:07:08.82,EN,,0,0,0,,And that's your compiled code.
Dialogue: 0,0:07:08.82,0:07:10.24,EN,,0,0,0,,So let me give you an example of that.
Dialogue: 0,0:07:12.70,0:07:14.14,EN,,0,0,0,,Suppose we're going to compile--
Dialogue: 0,0:07:15.10,0:07:17.90,EN,,0,0,0,,Suppose we want to compile the expression f of x.
Dialogue: 0,0:07:25.07,0:07:25.96,EN,,0,0,0,,So let's assume that
Dialogue: 0,0:07:25.96,0:07:28.06,EN,,0,0,0,,we've got f of x in the exp register
Dialogue: 0,0:07:28.06,0:07:29.55,EN,,0,0,0,,and something in the environment register.
Dialogue: 0,0:07:30.10,0:07:32.20,EN,,0,0,0,,And now imagine starting up the evaluator.
Dialogue: 0,0:07:34.60,0:07:35.71,EN,,0,0,0,,Well, it looks at the expression
Dialogue: 0,0:07:35.71,0:07:37.36,EN,,0,0,0,,and it sees that it's an application.
Dialogue: 0,0:07:37.92,0:07:41.90,EN,,0,0,0,,And it branches to a place in the
Dialogue: 0,0:07:42.52,0:07:45.15,EN,,0,0,0,,in the evaluator code we saw called ev-application.
Dialogue: 0,0:07:47.12,0:07:48.12,EN,,0,0,0,,And then it begins.
Dialogue: 0,0:07:48.16,0:07:50.08,EN,,0,0,0,,It stores away the operands and unev
Dialogue: 0,0:07:50.08,0:07:52.44,EN,,0,0,0,,and then it's going to put the operator in exp,
Dialogue: 0,0:07:52.48,0:07:54.27,EN,,0,0,0,,and it's going to go recursively evaluate it.
Dialogue: 0,0:07:54.47,0:07:56.08,EN,,0,0,0,,That's the process that we walk through.
Dialogue: 0,0:07:56.67,0:07:57.84,EN,,0,0,0,,And if you start looking at the code,
Dialogue: 0,0:07:57.87,0:07:59.74,EN,,0,0,0,,you start seeing some register operations.
Dialogue: 0,0:08:00.20,0:08:02.30,EN,,0,0,0,,You see assign to unev the operands
Dialogue: 0,0:08:02.30,0:08:03.95,EN,,0,0,0,,assign to exp the operator,
Dialogue: 0,0:08:04.09,0:08:06.20,EN,,0,0,0,,save the environment, generate that, and so on.
Dialogue: 0,0:08:10.22,0:08:11.93,EN,,0,0,0,,Well, if we look on the overhead here
Dialogue: 0,0:08:15.75,0:08:19.58,EN,,0,0,0,,we can see those operations starting to be produced.
Dialogue: 0,0:08:20.82,0:08:22.52,EN,,0,0,0,,Here's sort of the first real operation
Dialogue: 0,0:08:22.72,0:08:24.80,EN,,0,0,0,,that the evaluator would have done.
Dialogue: 0,0:08:25.00,0:08:27.20,EN,,0,0,0,,It pulls the operands out of the exp register
Dialogue: 0,0:08:27.47,0:08:28.62,EN,,0,0,0,,and assigns it to unev.
Dialogue: 0,0:08:30.03,0:08:32.27,EN,,0,0,0,,And then it assigns something to the expression register,
Dialogue: 0,0:08:32.30,0:08:33.46,EN,,0,0,0,,and it saves continue
Dialogue: 0,0:08:33.46,0:08:34.62,EN,,0,0,0,,and it saves env.
Dialogue: 0,0:08:34.62,0:08:38.65,EN,,0,0,0,,And all I'm doing here is writing down the register assignments
Dialogue: 0,0:08:39.57,0:08:42.32,EN,,0,0,0,,that the evaluator would have done in executing that code.
Dialogue: 0,0:08:42.77,0:08:43.79,EN,,0,0,0,,And can zoom out a little bit.
Dialogue: 0,0:08:44.30,0:08:47.13,EN,,0,0,0,,Altogether, there are about 19 operations there.
Dialogue: 0,0:08:49.40,0:08:51.64,EN,,0,0,0,,And this is the--this will be the piece of code
Dialogue: 0,0:08:52.05,0:08:53.90,EN,,0,0,0,,up until the point where
Dialogue: 0,0:08:54.75,0:08:57.10,EN,,0,0,0,,the evaluator branches off to apply-dispatch.
Dialogue: 0,0:08:57.86,0:08:59.16,EN,,0,0,0,,And in fact, in this compiler
Dialogue: 0,0:08:59.20,0:09:01.18,EN,,0,0,0,,we're not going to worry about apply-dispatch at all.
Dialogue: 0,0:09:01.30,0:09:02.11,EN,,0,0,0,,We're going to have everything
Dialogue: 0,0:09:02.35,0:09:05.04,EN,,0,0,0,,we're going to have both interpreted code and compiled code.
Dialogue: 0,0:09:06.07,0:09:07.61,EN,,0,0,0,,Always evaluate procedures,
Dialogue: 0,0:09:07.61,0:09:09.85,EN,,0,0,0,,always apply procedures by going to apply-dispatch.
Dialogue: 0,0:09:10.27,0:09:12.32,EN,,0,0,0,,That will easily allow interpreted code and
Dialogue: 0,0:09:12.36,0:09:13.71,EN,,0,0,0,,compiled code to call each other.
Dialogue: 0,0:09:18.27,0:09:19.87,EN,,0,0,0,,Well, in principle, that's all we need to do.
Dialogue: 0,0:09:21.05,0:09:22.66,EN,,0,0,0,,You just run the evaluator.
Dialogue: 0,0:09:22.66,0:09:24.50,EN,,0,0,0,,So the compiler's a lot like the evaluator.
Dialogue: 0,0:09:24.50,0:09:26.47,EN,,0,0,0,,You run it, except it stashes away these operations
Dialogue: 0,0:09:26.47,0:09:28.40,EN,,0,0,0,,instead of actually executing them.
Dialogue: 0,0:09:29.35,0:09:31.39,EN,,0,0,0,,Well, that's not, that's not quite true. there's
Dialogue: 0,0:09:32.91,0:09:34.99,EN,,0,0,0,,There's only one little lie in that.
Dialogue: 0,0:09:36.24,0:09:39.29,EN,,0,0,0,,What you have to worry about is if you have a, a predicate.
Dialogue: 0,0:09:40.12,0:09:42.16,EN,,0,0,0,,If you have some kind of test you want to do
Dialogue: 0,0:09:43.45,0:09:46.03,EN,,0,0,0,,obviously, at the point when you're compiling it
Dialogue: 0,0:09:46.52,0:09:47.98,EN,,0,0,0,,you don't know which branch of these--
Dialogue: 0,0:09:48.32,0:09:50.14,EN,,0,0,0,,of a conditional like this you're going to do.
Dialogue: 0,0:09:51.13,0:09:53.92,EN,,0,0,0,,So you can't say which one the evaluator would have done.
Dialogue: 0,0:09:54.90,0:09:57.12,EN,,0,0,0,,So all you do there is very simple.
Dialogue: 0,0:09:57.12,0:09:58.49,EN,,0,0,0,,You compile both branches.
Dialogue: 0,0:09:59.32,0:10:01.29,EN,,0,0,0,,So you compile a structure that looks like this.
Dialogue: 0,0:10:02.00,0:10:03.98,EN,,0,0,0,,That'll compile into something that says,
Dialogue: 0,0:10:05.31,0:10:09.15,EN,,0,0,0,,the code, the code for P.
Dialogue: 0,0:10:10.71,0:10:16.51,EN,,0,0,0,,And it puts its results in, say, the val register.
Dialogue: 0,0:10:18.17,0:10:20.64,EN,,0,0,0,,So you walk the interpreter over the predicate
Dialogue: 0,0:10:21.35,0:10:24.19,EN,,0,0,0,,and make sure that the result would go into the val register.
Dialogue: 0,0:10:24.70,0:10:27.22,EN,,0,0,0,,And then you compile an instruction that says
Dialogue: 0,0:10:27.22,0:10:33.79,EN,,0,0,0,,branch if, if val is true
Dialogue: 0,0:10:37.17,0:10:38.75,EN,,0,0,0,,to a place we'll call label one.
Dialogue: 0,0:10:44.97,0:10:47.52,EN,,0,0,0,,Then we, we will put the code for B
Dialogue: 0,0:10:49.42,0:10:52.32,EN,,0,0,0,,to walk the interpreter--walk the interpreter over B.
Dialogue: 0,0:10:53.62,0:10:57.21,EN,,0,0,0,,And then go to put in an instruction that says,
Dialogue: 0,0:10:57.23,0:10:58.75,EN,,0,0,0,,go to the next thing, whatever
Dialogue: 0,0:11:02.20,0:11:04.56,EN,,0,0,0,,whatever was 
Download .txt
gitextract_b_q_3yll/

├── .gitignore
├── Ass/
│   ├── lec10a.chn+eng.ass
│   ├── lec10a.chn.ass
│   ├── lec10a.eng.ass
│   ├── lec10b.chn+eng.ass
│   ├── lec10b.chn.ass
│   ├── lec10b.eng.ass
│   ├── lec1a.chn+eng.ass
│   ├── lec1a.chn.ass
│   ├── lec1a.eng.ass
│   ├── lec3a.chn+eng.ass
│   ├── lec3a.chn.ass
│   ├── lec3a.eng.ass
│   ├── lec3b.chn+eng.ass
│   ├── lec3b.chn.ass
│   ├── lec3b.eng.ass
│   ├── lec4a.chn+eng.ass
│   ├── lec4a.chn.ass
│   ├── lec4a.eng.ass
│   ├── lec4b.chn+eng.ass
│   ├── lec4b.chn.ass
│   ├── lec4b.eng.ass
│   ├── lec5a.chn+eng.ass
│   ├── lec5a.chn.ass
│   ├── lec5a.eng.ass
│   ├── lec5b.chn+eng.ass
│   ├── lec5b.chn.ass
│   ├── lec5b.eng.ass
│   ├── lec6a.chn+eng.ass
│   ├── lec6a.chn.ass
│   ├── lec6a.eng.ass
│   ├── lec6b.chn+eng.ass
│   ├── lec6b.chn.ass
│   ├── lec6b.eng.ass
│   ├── lec7a.chn+eng.ass
│   ├── lec7a.chn.ass
│   ├── lec7a.eng.ass
│   ├── lec7b.chn+eng.ass
│   ├── lec7b.chn.ass
│   ├── lec7b.eng.ass
│   ├── lec8a.chn+eng.ass
│   ├── lec8a.chn.ass
│   ├── lec8a.eng.ass
│   ├── lec8b.chn+eng.ass
│   ├── lec8b.chn.ass
│   ├── lec8b.eng.ass
│   ├── lec9a.chn+eng.ass
│   ├── lec9a.chn.ass
│   ├── lec9a.eng.ass
│   ├── lec9b.chn+eng.ass
│   ├── lec9b.chn.ass
│   └── lec9b.eng.ass
├── Preface/
│   ├── pre1.txt
│   ├── pre2.txt
│   ├── pre3.txt
│   ├── pre4.txt
│   └── pre5.txt
├── README.md
├── SrtCN/
│   ├── lec10a.eng.srt
│   ├── lec10a.srt
│   ├── lec10b.eng.srt
│   ├── lec10b.srt
│   ├── lec1a.srt
│   ├── lec1b.srt
│   ├── lec2a.srt
│   ├── lec2b.srt
│   ├── lec3a.srt
│   ├── lec3b.srt
│   ├── lec4a.srt
│   ├── lec4b.srt
│   ├── lec5a.srt
│   ├── lec5b.srt
│   ├── lec6a.srt
│   ├── lec6b.srt
│   ├── lec7a.srt
│   ├── lec7b.srt
│   ├── lec8a.chn.srt
│   ├── lec8a.srt
│   ├── lec8b.eng.srt
│   ├── lec8b.srt
│   ├── lec9a.srt
│   ├── lec9b.eng.srt
│   └── lec9b.srt
├── SrtEN/
│   ├── lec10a_512kb.mp4.srt
│   ├── lec10b_512kb.mp4.srt
│   ├── lec1a_512kb.mp4.srt
│   ├── lec1b_512kb.mp4.srt
│   ├── lec2a_512kb.mp4.srt
│   ├── lec2b_512kb.mp4.srt
│   ├── lec3a_512kb.mp4.srt
│   ├── lec3b_512kb.mp4.srt
│   ├── lec4a_512kb.mp4.srt
│   ├── lec4b_512kb.mp4.srt
│   ├── lec5a_512kb.mp4.srt
│   ├── lec5b_512kb.mp4.srt
│   ├── lec6a_512kb.mp4.srt
│   ├── lec6b_512kb.mp4.srt
│   ├── lec7a_512kb.mp4.srt
│   ├── lec7b_512kb.mp4.srt
│   ├── lec8a_512kb.mp4.srt
│   ├── lec8b_512kb.mp4.srt
│   ├── lec9a_512kb.mp4.srt
│   └── lec9b_512kb.mp4.srt
├── Sub/
│   ├── lec10a.txt
│   ├── lec10b.txt
│   ├── lec1a.txt
│   ├── lec1b.txt
│   ├── lec2a.txt
│   ├── lec2b.txt
│   ├── lec3a.txt
│   ├── lec3b.txt
│   ├── lec4a.txt
│   ├── lec4b.txt
│   ├── lec5a.txt
│   ├── lec5b.txt
│   ├── lec6a.txt
│   ├── lec6b.txt
│   ├── lec7a.txt
│   ├── lec7b.txt
│   ├── lec8a.txt
│   ├── lec8b.txt
│   ├── lec9a.txt
│   └── lec9b.txt
└── Tools/
    ├── con2table.rb
    ├── contributor.json
    ├── download-sicp-movies.sh
    ├── lec.json
    ├── lec2tabel.rb
    ├── merge.rb
    ├── pdf2txt.rb
    ├── separate.rb
    ├── split.pl
    ├── split.rb
    ├── timeline.rb
    └── util.rb
Download .txt
SYMBOL INDEX (14 symbols across 5 files)

FILE: Tools/con2table.rb
  function avatar (line 6) | def avatar(contributor)
  function name (line 10) | def name(contributor)

FILE: Tools/lec2tabel.rb
  function render_authors (line 25) | def render_authors(authors)
  function render_as_mdlink (line 33) | def render_as_mdlink(text, link=nil)
  function render_as_mdpic (line 37) | def render_as_mdpic(alt, link)
  function render_as_mdpiclink (line 41) | def render_as_mdpiclink(picurl, text, link)
  function itemize (line 45) | def itemize(row)
  function table_head (line 57) | def table_head
  function divider (line 61) | def divider

FILE: Tools/merge.rb
  class IO (line 10) | class IO
    method each_unit (line 12) | def each_unit(lines)

FILE: Tools/split.rb
  function help (line 5) | def help

FILE: Tools/util.rb
  function backup_filename (line 4) | def backup_filename(filename)
  function backup_file (line 8) | def backup_file(filename)
Copy disabled (too large) Download .json
Condensed preview — 135 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (11,069K chars).
[
  {
    "path": ".gitignore",
    "chars": 62,
    "preview": "*.*~\n#*#\n.DS_Store\nThumbs.db\n*.backup\nTools/*.srt\nTools/*.ass\n"
  },
  {
    "path": "Ass/lec10a.chn+eng.ass",
    "chars": 125347,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec10a.chn.ass",
    "chars": 54049,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec10a.eng.ass",
    "chars": 72814,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec10b.chn+eng.ass",
    "chars": 183548,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec10b.chn.ass",
    "chars": 81720,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec10b.eng.ass",
    "chars": 104009,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec1a.chn+eng.ass",
    "chars": 168746,
    "preview": "[Script Info]\r\n; Script generated by Aegisub 3.0.2\r\n; http://www.aegisub.org/\r\nTitle: Default Aegisub file\r\nScriptType:"
  },
  {
    "path": "Ass/lec1a.chn.ass",
    "chars": 70135,
    "preview": "[Script Info]\r\n; Script generated by Aegisub 3.0.2\r\n; http://www.aegisub.org/\r\nTitle: Default Aegisub file\r\nScriptType:"
  },
  {
    "path": "Ass/lec1a.eng.ass",
    "chars": 104503,
    "preview": "[Script Info]\r\n; Script generated by Aegisub 3.0.2\r\n; http://www.aegisub.org/\r\nTitle: Default Aegisub file\r\nScriptType:"
  },
  {
    "path": "Ass/lec3a.chn+eng.ass",
    "chars": 160007,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: SICP\nScriptType: v4.00+\nWrapStyle: 0"
  },
  {
    "path": "Ass/lec3a.chn.ass",
    "chars": 63852,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec3a.eng.ass",
    "chars": 97780,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: SICP\nScriptType: v4.00+\nWrapStyle: 0"
  },
  {
    "path": "Ass/lec3b.chn+eng.ass",
    "chars": 97616,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec3b.chn.ass",
    "chars": 40308,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec3b.eng.ass",
    "chars": 59034,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec4a.chn+eng.ass",
    "chars": 139178,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: CHNENG Aegisub file\nScriptType: v4.0"
  },
  {
    "path": "Ass/lec4a.chn.ass",
    "chars": 55884,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: CHN Aegisub file\nScriptType: v4.00+\n"
  },
  {
    "path": "Ass/lec4a.eng.ass",
    "chars": 84468,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.0.4\n; http://www.aegisub.org/\nTitle: EN Aegisub file\nScriptType: v4.00+\nW"
  },
  {
    "path": "Ass/lec4b.chn+eng.ass",
    "chars": 211901,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec4b.chn.ass",
    "chars": 89818,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec4b.eng.ass",
    "chars": 123045,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec5a.chn+eng.ass",
    "chars": 188964,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec5a.chn.ass",
    "chars": 81279,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec5a.eng.ass",
    "chars": 109892,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: EN Aegisub file\nScriptType: v4.00+\nW"
  },
  {
    "path": "Ass/lec5b.chn+eng.ass",
    "chars": 150379,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: EN Aegisub file\nScriptType: v4.00+\nWr"
  },
  {
    "path": "Ass/lec5b.chn.ass",
    "chars": 64232,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec5b.eng.ass",
    "chars": 87566,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: EN Aegisub file\nScriptType: v4.00+\nW"
  },
  {
    "path": "Ass/lec6a.chn+eng.ass",
    "chars": 184859,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: EN Aegisub file\nScriptType: v4.00+\nWr"
  },
  {
    "path": "Ass/lec6a.chn.ass",
    "chars": 80076,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4.0"
  },
  {
    "path": "Ass/lec6a.eng.ass",
    "chars": 105926,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: EN Aegisub file\nScriptType: v4.00+\nWr"
  },
  {
    "path": "Ass/lec6b.chn+eng.ass",
    "chars": 173270,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec6b.chn.ass",
    "chars": 74530,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec6b.eng.ass",
    "chars": 99828,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec7a.chn+eng.ass",
    "chars": 204251,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec7a.chn.ass",
    "chars": 89063,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec7a.eng.ass",
    "chars": 116546,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec7b.chn+eng.ass",
    "chars": 144937,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec7b.chn.ass",
    "chars": 62738,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec7b.eng.ass",
    "chars": 83419,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec8a.chn+eng.ass",
    "chars": 105624,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4.0"
  },
  {
    "path": "Ass/lec8a.chn.ass",
    "chars": 46146,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec8a.eng.ass",
    "chars": 60540,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec8b.chn+eng.ass",
    "chars": 184760,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec8b.chn.ass",
    "chars": 79376,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec8b.eng.ass",
    "chars": 106917,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec9a.chn+eng.ass",
    "chars": 186801,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec9a.chn.ass",
    "chars": 81235,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec9a.eng.ass",
    "chars": 106735,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: EN Aegisub file\nScriptType: v4.00+\nW"
  },
  {
    "path": "Ass/lec9b.chn+eng.ass",
    "chars": 204118,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec9b.chn.ass",
    "chars": 89557,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Ass/lec9b.eng.ass",
    "chars": 115737,
    "preview": "[Script Info]\n; Script generated by Aegisub 3.2.2\n; http://www.aegisub.org/\nTitle: Default Aegisub file\nScriptType: v4."
  },
  {
    "path": "Preface/pre1.txt",
    "chars": 885,
    "preview": "心智的活动,除了尽力产生各种简单的认识之外,主要表现在如下三个方面:1)将若干简单认识组合为一个复合认识,由此产生出各种复杂的认识。2)将两个认识放在一起对照,不管它们如何简单或者复杂,在这样做时并不将它们合而为一。由此得到有关它们的相互关"
  },
  {
    "path": "Preface/pre2.txt",
    "chars": 463,
    "preview": "现在到了数学抽象中最关键的一步:让我们忘记这些符号所表示的对象。……(数学家)不应在这里停步,有许多操作可以应用与这些符号,而根本不必考虑它们到底代表着什么东西。\nHermann Weyl, The Mathematical Way of "
  },
  {
    "path": "Preface/pre3.txt",
    "chars": 191,
    "preview": "即使在变化中,它也丝毫未变。\n——赫拉克立特(Heraclitus)\n\n变得越多,它就越是原来的样子。\n——阿尔芬斯·卡尔(Alphonse Karr)\n\n\n\n(Even while it changes, it stands still."
  },
  {
    "path": "Preface/pre4.txt",
    "chars": 753,
    "preview": "用普通的话来说,这个咒语就是——阿巴拉卡达巴拉,芝麻开门,而且还有另外的东西——在一个故事里的咒语在另一故事里就不灵了。真正的魔力在于知道哪个咒语有用,在什么时候,用于做什么,其诀窍就在于学会有关的诀窍。\n而这些咒语也是用我们的字母表里的字"
  },
  {
    "path": "Preface/pre5.txt",
    "chars": 575,
    "preview": "我的目的是想说明,这一天空机器并不是一种天赐造物或者生命体,它只不过是钟表一类的机械装置(而那些相信钟表有灵魂的人却将这一工作说成是其创造者的荣耀),在很大程度上,这里多种多样的运动都是由最简单的物质力量产生的,就像钟表里所有活动都是由一个"
  },
  {
    "path": "README.md",
    "chars": 9589,
    "preview": "# 《计算机程序的结构和解释》公开课 翻译项目\n\n<img height=\"20px\" src=\"https://user-images.githubusercontent.com/895809/47278305-6d793380-d5fa"
  },
  {
    "path": "SrtCN/lec10a.eng.srt",
    "chars": 47226,
    "preview": "1\n00:00:05,580 --> 00:00:20,180\n[MUSIC PLAYING]\n\n2\n00:00:20,180 --> 00:00:36,640\nPROFESSOR: Last time, we took a look a"
  },
  {
    "path": "SrtCN/lec10a.srt",
    "chars": 73367,
    "preview": "1\n00:00:00,016 --> 00:00:05,024\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:05,376 --> 00:00:07,840\n翻译&&时间轴:杨启钊(windfarer)\n压制&&特效:邓"
  },
  {
    "path": "SrtCN/lec10b.eng.srt",
    "chars": 63368,
    "preview": "1\n00:00:04,970 --> 00:00:06,535\n[MUSIC-- \"JESU, JOY OF MAN'S DESIRING\" BY JOHANN SEBASTIAN BACH]\n\n2\n00:00:18,910 --> 00"
  },
  {
    "path": "SrtCN/lec10b.srt",
    "chars": 103928,
    "preview": "1\n00:00:00,000 --> 00:00:00,720\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:00,720 --> 00:00:02,720\n翻译&&时间轴:杨启钊(windfarer)\n压制&&特效:邓"
  },
  {
    "path": "SrtCN/lec1a.srt",
    "chars": 108335,
    "preview": "1\r\n00:00:01,100 --> 00:00:04,500\r\n哈尔滨工业大学 IBM技术中心\r\n倾情制作\r\n\r\n2\r\n00:00:04,600 --> 00:00:09,400\r\n压制&&特效:蔡钟敏(Tacitus)\r\n字幕&&时"
  },
  {
    "path": "SrtCN/lec1b.srt",
    "chars": 99310,
    "preview": "1\n00:00:01,840 --> 00:00:02,800\n哈尔滨工业大学 IBM技术中心\n倾情制作\n\n2\n00:00:02,800 --> 00:00:04,800\n压制&&特效:蔡钟毓(JohnTitor)\n字幕&&时间轴:曹竞帆"
  },
  {
    "path": "SrtCN/lec2a.srt",
    "chars": 83242,
    "preview": "1\r\n00:00:00,000 --> 00:00:05,000\r\n哈尔滨工业大学 IBM技术中心\r\n倾情制作\r\n\r\n2\r\n00:00:05,100 --> 00:00:10,000\r\n压制&&特效:蔡钟毓(JohnTitor)\r\n翻译:"
  },
  {
    "path": "SrtCN/lec2b.srt",
    "chars": 114385,
    "preview": "1\r\n00:00:00,000 --> 00:00:04,000\r\n哈尔滨工业大学 IBM技术中心\r\n倾情制作\r\n\r\n2\r\n00:00:04,100 --> 00:00:08,000\r\n压制&&特效:蔡钟毓(JohnTitor)\r\n翻译&"
  },
  {
    "path": "SrtCN/lec3a.srt",
    "chars": 102580,
    "preview": "1\n00:00:00,000 --> 00:00:03,120\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:04,400 --> 00:00:08,026\n翻译&&时间轴:邓雄飞(Dysprosium)、Savior "
  },
  {
    "path": "SrtCN/lec3b.srt",
    "chars": 60269,
    "preview": "1\n00:00:00,000 --> 00:00:02,320\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:02,573 --> 00:00:06,013\n翻译&&时间轴:邓雄飞(Dysprosium)\n压制&&特效:"
  },
  {
    "path": "SrtCN/lec4a.srt",
    "chars": 87880,
    "preview": "1\n00:00:00,000 --> 00:00:22,340\n[music]\n\n2\n00:00:22,340 --> 00:00:24,340\n模式匹配:基于规则的代换\nPattern-matching: Rule-based Subs"
  },
  {
    "path": "SrtCN/lec4b.srt",
    "chars": 130004,
    "preview": "1\n00:00:00,000 --> 00:00:02,256\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:02,320 --> 00:00:03,536\n翻译&&时间轴:刘殊君(rtmagic)\n压制&&特效:邓雄飞"
  },
  {
    "path": "SrtCN/lec5a.srt",
    "chars": 112558,
    "preview": "1\n00:00:00,000 --> 00:00:01,968\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:01,980 --> 00:00:04,560\n翻译&&时间轴:杨启钊(windfarer)\n压制&&特效:邓"
  },
  {
    "path": "SrtCN/lec5b.srt",
    "chars": 90865,
    "preview": "1\n00:00:00,016 --> 00:00:02,464\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:02,608 --> 00:00:05,376\n翻译&&时间轴:张大伟(DreamAndDead)\n压制&&特"
  },
  {
    "path": "SrtCN/lec6a.srt",
    "chars": 109083,
    "preview": "1\n00:00:00,000 --> 00:00:02,704\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:02,730 --> 00:00:04,200\n翻译&&时间轴:张大伟(DreamAndDead)\n压制&&特"
  },
  {
    "path": "SrtCN/lec6b.srt",
    "chars": 103004,
    "preview": "1\n00:00:00,032 --> 00:00:03,728\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:03,984 --> 00:00:07,264\n翻译&&时间轴:张大伟(DreamAndDead)\n压制&&特"
  },
  {
    "path": "SrtCN/lec7a.srt",
    "chars": 119473,
    "preview": "1\n00:00:00,030 --> 00:00:02,688\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:02,752 --> 00:00:05,968\n翻译&&时间轴:邓雄飞、张大伟\n压制&&特效:邓雄飞(Dysp"
  },
  {
    "path": "SrtCN/lec7b.srt",
    "chars": 84918,
    "preview": "1\n00:00:00,010 --> 00:00:01,632\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:01,630 --> 00:00:04,336\n翻译&&时间轴:邓雄飞、张大伟\n压制&&特效:邓雄飞(Dysp"
  },
  {
    "path": "SrtCN/lec8a.chn.srt",
    "chars": 32201,
    "preview": "1\n00:00:00,000 --> 00:00:17,814\n【背景音乐:巴赫】\n\n2\n00:00:17,814 --> 00:00:22,132\n教授:上次课我们学习了如何构建一门语言。\n\n3\n00:00:22,132 --> 00:"
  },
  {
    "path": "SrtCN/lec8a.srt",
    "chars": 62881,
    "preview": "1\n00:00:00,000 --> 00:00:02,672\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:18,270 --> 00:00:19,680\n教授:上节课中 我们学习了\nPROFESSOR: The la"
  },
  {
    "path": "SrtCN/lec8b.eng.srt",
    "chars": 71750,
    "preview": "1\n00:00:18,910 --> 00:00:22,502\nPROFESSOR: All right, well, we've seen how the query language works.\n\n2\n00:00:22,502 --"
  },
  {
    "path": "SrtCN/lec8b.srt",
    "chars": 110473,
    "preview": "1\n00:00:00,000 --> 00:00:02,144\nLearning-SICP 学习小组\n倾情制作\n\n2\n00:00:02,400 --> 00:00:05,600\n《计算机程序的构造和解释》\n\n3\n00:00:18,910 "
  },
  {
    "path": "SrtCN/lec9a.srt",
    "chars": 109724,
    "preview": "1\n00:00:00,032 --> 00:00:02,048\nLearning-SICP学习小组\n倾情制作\n\n2\n00:00:02,040 --> 00:00:06,160\n翻译&&时间轴:邓雄飞\n压制&&特效:邓雄飞(Dysprosi"
  },
  {
    "path": "SrtCN/lec9b.eng.srt",
    "chars": 74227,
    "preview": "1\n00:00:15,840 --> 00:00:29,730\nPROFESSOR: Well, I hope you appreciate that we have inducted you into some real magic, "
  },
  {
    "path": "SrtCN/lec9b.srt",
    "chars": 119192,
    "preview": "1\n00:00:16,300 --> 00:00:18,080\n教授:我想大家已经意识到\nPROFESSOR: Well, I hope you appreciate that we have\n\n2\n00:00:20,010 --> 00"
  },
  {
    "path": "SrtEN/lec10a_512kb.mp4.srt",
    "chars": 63427,
    "preview": "0\n00:00:00,000 --> 00:00:05,580\n\n\n1\n00:00:05,580 --> 00:00:20,180\n[MUSIC PLAYING]\n\n2\n00:00:20,180 --> 00:00:23,920\nPROFE"
  },
  {
    "path": "SrtEN/lec10b_512kb.mp4.srt",
    "chars": 81567,
    "preview": "0\n00:00:00,000 --> 00:00:04,970\n\n\n1\n00:00:04,970 --> 00:00:05,285\n[MUSIC-- \"JESU, JOY OF\nMAN'S DESIRING\" BY\n\n2\n00:00:05,"
  },
  {
    "path": "SrtEN/lec1a_512kb.mp4.srt",
    "chars": 95057,
    "preview": "0\n00:00:00,000 --> 00:00:03,880\n\n\n1\n00:00:03,880 --> 00:00:14,550\n[MUSIC PLAYING]\n\n2\n00:00:14,550 --> 00:00:15,790\nPROFE"
  },
  {
    "path": "SrtEN/lec1b_512kb.mp4.srt",
    "chars": 83468,
    "preview": "1\n00:00:00,000 --> 00:00:14,550\n[MUSIC PLAYING BY J.S. BACH]\n\n2\n00:00:14,550 --> 00:00:16,320\nPROFESSOR: Hi.\n\n3\n00:00:16"
  },
  {
    "path": "SrtEN/lec2a_512kb.mp4.srt",
    "chars": 75424,
    "preview": "0\n00:00:00,000 --> 00:00:25,680\n\n\n1\n00:00:25,680 --> 00:00:27,960\nPROFESSOR: Well, yesterday\nwas easy.\n\n2\n00:00:27,960 -"
  },
  {
    "path": "SrtEN/lec2b_512kb.mp4.srt",
    "chars": 102358,
    "preview": "0\n00:00:00,000 --> 00:00:05,892\n\n\n1\n00:00:05,892 --> 00:00:22,120\n[MUSIC PLAYING]\n\n2\n00:00:22,120 --> 00:00:24,170\nPROFE"
  },
  {
    "path": "SrtEN/lec3a_512kb.mp4.srt",
    "chars": 97577,
    "preview": "0\n00:00:00,000 --> 00:00:04,470\n\n\n1\n00:00:04,470 --> 00:00:21,130\n[MUSIC PLAYING]\n\n2\n00:00:21,130 --> 00:00:24,770\nPROFE"
  },
  {
    "path": "SrtEN/lec3b_512kb.mp4.srt",
    "chars": 57469,
    "preview": "0\n00:00:00,000 --> 00:00:02,928\n\n\n1\n00:00:02,928 --> 00:00:19,520\n[MUSIC PLAYING]\n\n2\n00:00:19,520 --> 00:00:22,720\nPROFE"
  },
  {
    "path": "SrtEN/lec4a_512kb.mp4.srt",
    "chars": 87208,
    "preview": "0\n00:00:00,000 --> 00:00:24,460\n\n\n1\n00:00:24,460 --> 00:00:28,270\nPROFESSOR: Well, yesterday we\nlearned a bit about symb"
  },
  {
    "path": "SrtEN/lec4b_512kb.mp4.srt",
    "chars": 116567,
    "preview": "0\n00:00:00,000 --> 00:00:03,936\n\n\n1\n00:00:03,936 --> 00:00:04,279\n[MUSIC-- \"JESU, JOY OF\nMAN'S DESIRING\" BY\n\n2\n00:00:04,"
  },
  {
    "path": "SrtEN/lec5a_512kb.mp4.srt",
    "chars": 97914,
    "preview": "1\n00:00:00,000 --> 00:00:16,830\n[MUSIC PLAYING]\n\n2\n00:00:16,830 --> 00:00:22,480\nPROFESSOR: Well, so far we've\ninvented "
  },
  {
    "path": "SrtEN/lec5b_512kb.mp4.srt",
    "chars": 81897,
    "preview": "0\n00:00:00,000 --> 00:00:21,170\n\n\n1\n00:00:21,170 --> 00:00:24,550\nPROFESSOR: Well, now that we've\ngiven you some power t"
  },
  {
    "path": "SrtEN/lec6a_512kb.mp4.srt",
    "chars": 92051,
    "preview": "0\n00:00:00,000 --> 00:00:18,550\n\n\n1\n00:00:18,550 --> 00:00:21,230\nPROFESSOR: Well, last time Gerry\nreally let the cat ou"
  },
  {
    "path": "SrtEN/lec6b_512kb.mp4.srt",
    "chars": 88686,
    "preview": "0\n00:00:00,000 --> 00:00:20,970\n\n\n1\n00:00:20,970 --> 00:00:24,580\nPROFESSOR: OK, well, we've been\nlooking at streams, th"
  },
  {
    "path": "SrtEN/lec7a_512kb.mp4.srt",
    "chars": 102485,
    "preview": "0\n00:00:00,000 --> 00:00:15,314\n\n\n1\n00:00:15,314 --> 00:00:17,580\nPROFESSOR: Well today we're\ngoing to learn about somet"
  },
  {
    "path": "SrtEN/lec7b_512kb.mp4.srt",
    "chars": 72126,
    "preview": "0\n00:00:00,000 --> 00:00:00,994\n\n\n1\n00:00:00,994 --> 00:00:16,401\n[MUSIC PLAYING]\n\n2\n00:00:16,401 --> 00:00:19,520\nPROFE"
  },
  {
    "path": "SrtEN/lec8a_512kb.mp4.srt",
    "chars": 54700,
    "preview": "1\n00:00:00,000 --> 00:00:17,814\n[MUSIC PLAYING BY J.S. BACH]\n\n2\n00:00:17,814 --> 00:00:20,040\nPROFESSOR: The last time w"
  },
  {
    "path": "SrtEN/lec8b_512kb.mp4.srt",
    "chars": 94865,
    "preview": "0\n00:00:00,000 --> 00:00:18,910\n\n\n1\n00:00:18,910 --> 00:00:20,900\nPROFESSOR: All right, well,\nwe've seen how the query\n\n"
  },
  {
    "path": "SrtEN/lec9a_512kb.mp4.srt",
    "chars": 93992,
    "preview": "0\n00:00:00,000 --> 00:00:02,190\n\n\n1\n00:00:02,190 --> 00:00:02,550\n[MUSIC PLAYING - \"JESU, JOY OF\nMAN'S DESIRING\" BY JOHA"
  },
  {
    "path": "SrtEN/lec9b_512kb.mp4.srt",
    "chars": 96204,
    "preview": "1\r\n00:00:15,840 --> 00:00:20,260\r\nPROFESSOR: Well, I hope you appreciate that we have\r\n\r\n2\r\n00:00:20,260 --> 00:00:24,5"
  },
  {
    "path": "Sub/lec10a.txt",
    "chars": 34918,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec10b.txt",
    "chars": 44637,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec1a.txt",
    "chars": 52537,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec1b.txt",
    "chars": 45063,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec2a.txt",
    "chars": 40758,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec2b.txt",
    "chars": 56496,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec3a.txt",
    "chars": 55043,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec3b.txt",
    "chars": 31506,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec4a.txt",
    "chars": 47717,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec4b.txt",
    "chars": 63082,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec5a.txt",
    "chars": 52834,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec5b.txt",
    "chars": 44265,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec6a.txt",
    "chars": 108623,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec6b.txt",
    "chars": 48442,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec7a.txt",
    "chars": 53843,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec7b.txt",
    "chars": 39244,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec8a.txt",
    "chars": 30089,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec8b.txt",
    "chars": 52461,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec9a.txt",
    "chars": 50647,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Sub/lec9b.txt",
    "chars": 53192,
    "preview": "MIT OpenCourseWare\nhttp://ocw.mit.edu\n\n\n6.001 Structure and Interpretation of Computer Programs, Spring 2005\n\nTranscript"
  },
  {
    "path": "Tools/con2table.rb",
    "chars": 483,
    "preview": "#!/usr/bin/env ruby\n# *encoding: UTF-8*\n\nrequire 'json'\n\ndef avatar(contributor)\n  \"![](#{contributor['avatar']})\"\nend\n\n"
  },
  {
    "path": "Tools/contributor.json",
    "chars": 844,
    "preview": "[\n\t{\"name\": \"DeathKing\",\n\t \"avatar\": \"https://avatars0.githubusercontent.com/u/895809?s=120\"},\n\t{\"name\": \"ChingfanTsou\","
  },
  {
    "path": "Tools/download-sicp-movies.sh",
    "chars": 1952,
    "preview": "# Script by bjartwoIf\n# From https://gist.github.com/bjartwolf/3471090\nwget http://ia700402.us.archive.org/8/items/MIT_S"
  },
  {
    "path": "Tools/lec.json",
    "chars": 8936,
    "preview": "[\n    {\n        \"id\":         \"Lec1a\",\n        \"title\":      \"Lisp概览\",\n        \"youku\":      \"https://v.youku.com/v_show"
  },
  {
    "path": "Tools/lec2tabel.rb",
    "chars": 1915,
    "preview": "#!/usr/bin/env ruby\n# *encoding: UTF-8*\n\nrequire 'json'\n\nAUTHORS = {\n  \"ChingfanTsou\"   => \"https://github.com/ChingfanT"
  },
  {
    "path": "Tools/merge.rb",
    "chars": 1424,
    "preview": "#!/usr/bin/env ruby\n\n# MERGE.RB: merge two subtitles into one.\n#\n# usage: merge file1 file2 [output_file]\n#\n# options: -"
  },
  {
    "path": "Tools/pdf2txt.rb",
    "chars": 1008,
    "preview": "#!/usr/bin/env ruby\n\n# Gist from: https://gist.github.com/blazeeboy/9722831\n\n#begin\n  require 'pdf/reader'\n  require 'fi"
  },
  {
    "path": "Tools/separate.rb",
    "chars": 1052,
    "preview": "#!/usr/bin/env ruby\n# encoding: utf-8\n\nif ARGV.empty?\n    puts \"Separate - 分割单个双语字幕为两个字幕。\"\n    puts \"用法: separate srt_fi"
  },
  {
    "path": "Tools/split.pl",
    "chars": 399,
    "preview": "#! /usr/bin/perl\nuse 5.010;\nuse utf8;\nuse warnings;\nuse strict;\n\nmy $srt_file = shift(@ARGV);\nopen SRT, '<:encoding(UTF-"
  },
  {
    "path": "Tools/split.rb",
    "chars": 273,
    "preview": "#!/usr/bin/env ruby\n# encoding: utf-8\nrequire_relative 'util'\n\ndef help\n  doc = <<-HELP\n\n  HELP\nend\n\n\nfilename = ARGV.fi"
  },
  {
    "path": "Tools/timeline.rb",
    "chars": 725,
    "preview": "#!/usr/bin/env ruby\n# --* encoding: utf-8 *--\nrequire 'fileutils'\n\ncont = []\nblock = []\ncounter, flag = 1, false\nnewline"
  },
  {
    "path": "Tools/util.rb",
    "chars": 186,
    "preview": "# --* encoding: utf-8 *--\nrequire 'fileutils'\n\ndef backup_filename(filename)\n  filename + \".backup\"\nend\n\ndef backup_file"
  }
]

About this extraction

This page contains the full source code of the DeathKing/Learning-SICP GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 135 files (10.2 MB), approximately 2.7M tokens, and a symbol index with 14 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!