From 16930482add5493890edd33a6e49c928f66b87f4 Mon Sep 17 00:00:00 2001 From: Michael DiBernardo Date: Mon, 27 Jun 2016 08:39:05 -0400 Subject: [PATCH] Fix issues from @amyrbrown's amazing proofread. - https://github.com/aosabook/500lines/issues/228 --- contingent/contingent.markdown | 6 ++--- crawler/crawler.markdown | 4 ++-- dagoba/dagoba.markdown | 23 +++++++++++-------- functionalDB/functionalDB.markdown | 2 +- minutiae/colophon.tex | 4 ++-- minutiae/frontmatter.tex | 6 ++--- minutiae/intro.tex | 4 ++-- ocr/ocr.markdown | 2 +- pedometer/pedometer.markdown | 2 +- .../same-origin-policy.markdown | 4 ++-- sampler/sampler.markdown | 10 ++++---- spreadsheet/spreadsheet.markdown | 4 ++-- template-engine/template-engine.markdown | 7 +++--- web-server/web-server.markdown | 8 +++---- 14 files changed, 45 insertions(+), 41 deletions(-) diff --git a/contingent/contingent.markdown b/contingent/contingent.markdown index 8fa08c2cb..397ff21aa 100644 --- a/contingent/contingent.markdown +++ b/contingent/contingent.markdown @@ -1155,17 +1155,15 @@ The wrapper can access the original version of the function via the name `function`, calling it at the appropriate time. The body of the Contingent wrapper -runs something like this: \newpage +runs something like this: ```python def task(function): @wraps(function) def wrapper(*args): task = Task(wrapper, args) - if self.task_stack: self._graph.add_edge(task, self.task_stack[-1]) - self._graph.clear_inputs_of(task) self._task_stack.append(task) try: @@ -1347,7 +1345,7 @@ under the name `_` for use in the subsequent expression.) This recursive task of looking repeatedly for immediate consequences and only stopping when we arrive at tasks with no further consequences is a basic enough graph operation that it is supported directly -by a method on the `Graph` class: \newpage +by a method on the `Graph` class: ```python >>> # Secretly adjust pprint to a narrower-than-usual width: diff --git a/crawler/crawler.markdown b/crawler/crawler.markdown index f3b24928c..aa2cea038 100644 --- a/crawler/crawler.markdown +++ b/crawler/crawler.markdown @@ -342,8 +342,6 @@ The `foo` function loads `bar` onto its stack and calls it, then pops its return When `PyEval_EvalFrameEx` encounters the `CALL_FUNCTION` bytecode, it creates a new Python stack frame and recurses: that is, it calls `PyEval_EvalFrameEx` recursively with the new frame, which is used to execute `bar`. -\aosafigure[240pt]{crawler-images/function-calls.png}{Function Calls}{500l.crawler.functioncalls} - It is crucial to understand that Python stack frames are allocated in heap memory! The Python interpreter is a normal C program, so its stack frames are normal stack frames. But the *Python* stack frames it manipulates are on the heap. Among other surprises, this means a Python stack frame can outlive its function call. To see this interactively, save the current frame from within `bar`: ```python @@ -366,6 +364,8 @@ It is crucial to understand that Python stack frames are allocated in heap memor 'foo' ``` +\aosafigure[240pt]{crawler-images/function-calls.png}{Function Calls}{500l.crawler.functioncalls} + The stage is now set for Python generators, which use the same building blocks—code objects and stack frames—to marvelous effect. This is a generator function: diff --git a/dagoba/dagoba.markdown b/dagoba/dagoba.markdown index 8fc1614e0..1bc0d9745 100644 --- a/dagoba/dagoba.markdown +++ b/dagoba/dagoba.markdown @@ -11,9 +11,9 @@ _[Dann](https://twitter.com/dann) enjoys building things, like programming langu > "What went forth to the ends of the world to traverse not itself, God, the sun, Shakespeare, a commercial traveller, having itself traversed in reality itself becomes that self." > —James Joyce -A long time ago, when the world was still young, all data walked happily in single file. If you wanted your data to jump over a fence, you just set the fence down in its path and each datum jumped it in turn. Punch cards in, punch cards out. Life was easy and programming was a breeze. +\noindent A long time ago, when the world was still young, all data walked happily in single file. If you wanted your data to jump over a fence, you just set the fence down in its path and each datum jumped it in turn. Punch cards in, punch cards out. Life was easy and programming was a breeze. -Then came the random access revolution, and data grazed freely across the hillside. Herding data became a serious concern: if you can access any piece of data at any time, how do you know which one to pick next? Techniques were developed for corralling the data by forming links between items [^items], marshaling groups of units into formation through their linking assemblage. Questioning data meant picking a sheep and pulling along everything connected to it. +Then came the random access revolution, and data grazed freely across the hillside. Herding data became a serious concern: if you can access any piece of data at any time, how do you know which one to pick next? Techniques were developed for corralling the data by forming links between items[^items], marshaling groups of units into formation through their linking assemblage. Questioning data meant picking a sheep and pulling along everything connected to it. Later programmers departed from this tradition, imposing a set of rules on how data would be aggregated[^relationaltheory]. Rather than tying disparate data directly together they would cluster by content, decomposing data into bite-sized pieces, collected in pens and collared with name tags. Questions were posed declaratively, resulting in accumulating pieces of partially decomposed data (a state the relationalists refer to as "normal") into a frankencollection returned to the programmer. @@ -28,7 +28,7 @@ The distributed revolution changed everything, again. Data broke free of spacial ## Take One -Within this chapter we're going to build a graph database[^dagoba]. As we build it we're going to explore the problem space, generate multiple solutions for our design decisions, compare those solutions to understand the tradeoffs between them, and finally choose the right solution for our system. A higher-than-usual precedence is put on code compactness, but the process will otherwise mirror that used by software professionals since time immemorial. The purpose of this chapter is to teach this process. And to build a graph database.[^purpose] +Within this chapter we're going to build a graph database[^dagoba]. As we build it we're going to explore the problem space, generate multiple solutions for our design decisions, compare those solutions to understand the tradeoffs between them, and finally choose the right solution for our system. A higher-than-usual precedence is put on code compactness, but the process will otherwise mirror that used by software professionals since time immemorial. The purpose of this chapter is to teach this process. And to build a graph database[^purpose]. [^dagoba]: This database started life as a library for managing Directed Acyclic Graphs, or DAGs. Its name "Dagoba" was originally intended to come with a silent 'h' at the end, an homage to the swampy fictional planet, but reading the back of a chocolate bar one day we discovered the sans-h version refers to a place for silently contemplating the connections between things, which seems even more fitting. @@ -105,7 +105,7 @@ We're treating the edges as a global variable, which means we can only ever have We're also not using the vertices at all. What does that tell us? It implies that everything we need is in the edges array, which in this case is true: the vertex values are scalars, so they exist independently in the edges array. If we want to answer questions like "What is Freyja's connection to the Valkyries?" we'll need to add more data to the vertices, which means making them compound values, which means the edges array should reference vertices instead of copying their value. -The same holds true for our edges: they contain an "in" vertex and an "out" vertex [^vertexnote], but no elegant way to incorporate additional information. We'll need that to answer questions like "How many stepparents did Loki have?" or "How many children did Odin have before Thor was born?" +The same holds true for our edges: they contain an "in" vertex and an "out" vertex[^vertexnote], but no elegant way to incorporate additional information. We'll need that to answer questions like "How many stepparents did Loki have?" or "How many children did Odin have before Thor was born?" You don't have to squint very hard to tell that the code for our two selectors looks very similar, which suggests there may be a deeper abstraction from which they spring. @@ -145,7 +145,7 @@ Dagoba.graph = function(V, E) { // the factory } ``` -We'll accept two optional arguments: a list of vertices and a list of edges. JavaScript is rather lax about parameters, so all named parameters are optional and default to `undefined` if not supplied [^optionalparams]. We will often have the vertices and edges before building the graph and use the V and E parameters, but it's also common to not have those at creation time and to build the graph up programmatically [^graphbuilding]. +We'll accept two optional arguments: a list of vertices and a list of edges. JavaScript is rather lax about parameters, so all named parameters are optional and default to `undefined` if not supplied[^optionalparams]. We will often have the vertices and edges before building the graph and use the V and E parameters, but it's also common to not have those at creation time and to build the graph up programmatically[^graphbuilding]. [^optionalparams]: It's also lax in the other direction: all functions are variadic, and all arguments are available by position via the `arguments` object, which is almost like an array but not quite. ("Variadic" is a fancy way of saying a function has indefinite arity. "A function has indefinite arity" is a fancy way of saying it takes a variable number of variables.) @@ -176,7 +176,7 @@ Dagoba.G.addVertex = function(vertex) { // accepts a vertex-like object } ``` -If the vertex doesn't already have an `_id` property we assign it one using our autoid. [^autoid] If the `_id` already exists on a vertex in our graph then we reject the new vertex. Wait, when would that happen? And what exactly is a vertex? +If the vertex doesn't already have an `_id` property we assign it one using our autoid.[^autoid] If the `_id` already exists on a vertex in our graph then we reject the new vertex. Wait, when would that happen? And what exactly is a vertex? [^autoid]: Why can't we just use `this.vertices.length` here? @@ -414,13 +414,15 @@ There are a couple of common ways of determining this: in a statically typed sys [^referencecounter]: Most modern JS runtimes employ generational garbage collectors, and the language is intentionally kept at arm's length from the engine's memory management to curtail a source of programmatic non-determinism. -JavaScript doesn't have either of these facilities, but we can get almost the same effect if we're really, really disciplined. Which we will be. For now. \newpage +JavaScript doesn't have either of these facilities, but we can get almost the same effect if we're really, really disciplined. Which we will be. For now. #### In-N-Out Walking the graph is as easy as ordering a burger. These two lines set up the `in` and `out` pipetypes for us. +\newpage + ```javascript Dagoba.addPipetype('out', Dagoba.simpleTraversal('out')) Dagoba.addPipetype('in', Dagoba.simpleTraversal('in')) @@ -467,11 +469,14 @@ In this case, with a dozen or so pipetypes, the right choice seems to be to styl #### Property -Let's pause for a moment to consider an example query based on the three pipetypes we've seen. We can ask for Thor's grandparents like this: `g.v('Thor').out('parent').out('parent').run()` [^runnote]. But what if we wanted their names? +Let's pause for a moment to consider an example query based on the three pipetypes we've seen. We can ask for Thor's grandparents like this[^runnote]: [^runnote]: The `run()` at the end of the query invokes the interpreter and returns results. -We could put a map on the end of that: +```javascript +g.v('Thor').out('parent').out('parent').run() +``` +But what if we wanted their names? We could put a map on the end of that: ```javascript g.v('Thor').out('parent').out('parent').run() diff --git a/functionalDB/functionalDB.markdown b/functionalDB/functionalDB.markdown index 99c645e85..e98992d26 100644 --- a/functionalDB/functionalDB.markdown +++ b/functionalDB/functionalDB.markdown @@ -141,7 +141,7 @@ We will access the storage via a simple _protocol_, which will make it possible (drop-entity [storage entity])) ``` -And here's our in-memory implementation of the protocol, which uses a map as the store: +\noindent And here's our in-memory implementation of the protocol, which uses a map as the store: ```clojure (defrecord InMemory [] Storage diff --git a/minutiae/colophon.tex b/minutiae/colophon.tex index 91dc06f20..ae25bb46c 100644 --- a/minutiae/colophon.tex +++ b/minutiae/colophon.tex @@ -5,8 +5,8 @@ Heros, both by Bogus\l{}aw Jackowski and Janusz M. Nowacki. The code font is Inconsolata by Raph Levien. -The front cover photo is composed of 23 separate stack focused images of watch -gear assemblies. The picture was taken by Kellar Wilson. +The front cover photo is composed of twenty-three separate focus-stacked images +of watch gear assemblies. The picture was taken by Kellar Wilson. (\url{http://kellarwilson.smugmug.com/}) This book was built with open source software (with the exception of the diff --git a/minutiae/frontmatter.tex b/minutiae/frontmatter.tex index d9046570f..b3c59bbb7 100644 --- a/minutiae/frontmatter.tex +++ b/minutiae/frontmatter.tex @@ -35,7 +35,7 @@ \small \noindent \textbf{500 Lines or Less} \\ -Edited by Michael DiBernardo and Amy Brown +Edited by Michael DiBernardo \vspace{0.15cm} @@ -115,13 +115,13 @@ \vspace{0.15cm} \noindent Front cover photo \copyright Kellar Wilson \\ -\noindent Copyediting, cover design, and publishing support by Amy Brown: www.amyrbrown.ca +\noindent Copyediting, cover design, and publishing support by Amy Brown: \url{http://amyrbrown.ca} \vspace{1cm} \noindent Revision Date: \today \\ -\noindent ISBN: 978-1-304-48878-7 +\noindent ISBN: 978-1-329-87127-4 \normalsize \newpage diff --git a/minutiae/intro.tex b/minutiae/intro.tex index c0197a28b..849a7236d 100644 --- a/minutiae/intro.tex +++ b/minutiae/intro.tex @@ -1,7 +1,7 @@ \begin{aosachapter}{Introduction}{s:intro}{Michael DiBernardo} This is the fourth volume in the \emph{Architecture of Open Source Applications} -series, and the first to not feature the words `open source applications' anywhere in the +series, and the first to not feature the words ``open source applications'' anywhere in the title. The first three volumes in the series were about big problems that big programs @@ -61,7 +61,7 @@ \section*{Contributors} \emph{Taavi Burns (DBDB)}: As the newest bass (and sometimes tenor) in Countermeasure, Taavi strives to break the mould\ldots sometimes just by ignoring its existence. This is certainly true through the diversity of workplaces in his career: IBM (doing C and Perl), FreshBooks (all the things), Points.com (doing Python), and now at PagerDuty (doing Scala). Aside from that—when not gliding along on his Brompton folding bike—you might find him playing Minecraft with his son or engaging in parkour (or rock climbing, or other adventures) with his wife. He knits continental. -\emph{Leo Zovic}: Leo (better known online as inaimathi) is a recovering Graphic Designer who has professionally written Scheme, Common Lisp, Erlang, Javascript, Haskell, Clojure, Go, Python, PHP and C. He currently blogs about programming, plays board games and works at a Ruby-based startup in Toronto, Ontario. +\emph{Leo Zovic}: Leo (better known online as inaimathi) is a recovering graphic designer who has professionally written Scheme, Common Lisp, Erlang, Javascript, Haskell, Clojure, Go, Python, PHP and C. He currently blogs about programming, plays board games and works at a Ruby-based startup in Toronto, Ontario. \emph{Dr.\@ Christian Muise (Flow shop)}: Dr.\@ Muise is a Research Fellow with the Model-based Embedded and Robotic Systems group at MIT's Computer Science and Artificial Intelligence Laboratory. He is interested in a variety of topics including AI, data-driven projects, mapping, graph theory, and data visualization, as well as Celtic music, carving, soccer, and coffee. diff --git a/ocr/ocr.markdown b/ocr/ocr.markdown index 454ec9442..c79e6f74a 100644 --- a/ocr/ocr.markdown +++ b/ocr/ocr.markdown @@ -701,7 +701,7 @@ the digit predicted by the ANN. Many resources are available online that go into greater detail on the implementation of backpropagation. One good resource is from a [course by the University of -Williamette](http://www.willamette.edu/~gorr/classes/cs449/backprop.html). It +Willamette](http://www.willamette.edu/~gorr/classes/cs449/backprop.html). It goes over the steps of backpropagation and then explains how it can be translated into matrix form. While the amount of computation using matrices is the same as using loops, the benefit is that the code is simpler and easier to diff --git a/pedometer/pedometer.markdown b/pedometer/pedometer.markdown index a511db967..50e4616f8 100644 --- a/pedometer/pedometer.markdown +++ b/pedometer/pedometer.markdown @@ -216,7 +216,7 @@ When bumpiness occurs at our threshold, we can mistakenly count too many steps f \aosafigure[333pt]{pedometer-images/acceleration-filtered.png}{Tweaked peaks}{500l.pedometer.accelerationfiltered} -In accounting for these four scenarios, we've managed to bring our messy $a(t)$ fairly close to our ideal sine wave (\aosafigref{500l.pedometer.accelerationfiltered}), allowing us to count steps. +\noindent In accounting for these four scenarios, we've managed to bring our messy $a(t)$ fairly close to our ideal sine wave (\aosafigref{500l.pedometer.accelerationfiltered}), allowing us to count steps. ### Recap diff --git a/same-origin-policy/same-origin-policy.markdown b/same-origin-policy/same-origin-policy.markdown index da5aacc27..842b76ac4 100644 --- a/same-origin-policy/same-origin-policy.markdown +++ b/same-origin-policy/same-origin-policy.markdown @@ -1218,7 +1218,7 @@ and it is the responsibility of the receiving document to _additionally_ check the `srcOrigin` parameter to ensure that the message is coming from a trustworthy document. Unfortunately, in practice, many sites omit this check, enabling a malicious document to -inject bad content as part of a `PostMessage` [^postMessageStudy]. +inject bad content as part of a `PostMessage`[^postMessageStudy]. However, the omission of the origin check may not simply be the result of programmer ignorance. Implementing an appropriate check on an incoming @@ -1301,7 +1301,7 @@ resource on the server. This access pattern is appropriate if the resource is considered public and accessible to anyone. However, it turns out that many sites use "\*" as the default value even for private resources, inadvertently allowing malicious scripts to access -them through CORS requests [^corsStudy]. +them through CORS requests[^corsStudy]. Why would a developer ever use the wildcard? It turns out that specifying the allowed origins can be tricky, since it may not be diff --git a/sampler/sampler.markdown b/sampler/sampler.markdown index 080137f13..a348c4b82 100644 --- a/sampler/sampler.markdown +++ b/sampler/sampler.markdown @@ -244,7 +244,7 @@ as well). Before we get into the rest of the class, let's go over two points related to the constructor. -#### Descriptive vs. Mathematic Variable Names +#### Descriptive versus Mathematic Variable Names Usually, programmers are encouraged to use descriptive variable names: for example, it would be considered better practice to use the names @@ -333,10 +333,10 @@ numbers come from. We create it as follows: >>> rso = np.random.RandomState(230489) ``` -where the number passed to the `RandomState` constructor is the *seed* -for the random number generator. As long as we instantiate it with the -same seed, a `RandomState` object will produce the same "random" -numbers in the same order, thus ensuring replicability: +\noindent where the number passed to the `RandomState` constructor is the +*seed* for the random number generator. As long as we instantiate it with the +same seed, a `RandomState` object will produce the same "random" numbers in the +same order, thus ensuring replicability: ```python >>> rso.rand() diff --git a/spreadsheet/spreadsheet.markdown b/spreadsheet/spreadsheet.markdown index a0ace79ba..86e77923a 100644 --- a/spreadsheet/spreadsheet.markdown +++ b/spreadsheet/spreadsheet.markdown @@ -543,7 +543,7 @@ input:focus + div { white-space: nowrap; } ## Conclusion -Since this book is _500 Lines or Less_, a web spreadsheet in 99 lines is a minimal example — please feel free to experiment and extend it in any direction you’d like. +Since this book is _500 Lines or Less_, a web spreadsheet in 99 lines is a minimal example—please feel free to experiment and extend it in any direction you’d like. Here are some ideas, all easily reachable in the remaining space of 401 lines: @@ -563,4 +563,4 @@ For people preferring a cleaner syntax, the [as-livescript-1.3.0](https://audrey Building on the LiveScript language, the [as-react-livescript](https://audreyt.github.io/500lines/spreadsheet/as-react-livescript/) directory uses the [ReactJS](https://facebook.github.io/react/) framework; [it is 10 lines more longer](https://github.com/audreyt/500lines/tree/master/spreadsheet/as-react-livescript) than the AngularJS equivalent, but runs considerably faster. -If you are interested in translating this example to alternate JS languages, send a [pull request](https://github.com/audreyt/500lines/pulls) — I’d love to hear about it! +If you are interested in translating this example to alternate JS languages, send a [pull request](https://github.com/audreyt/500lines/pulls)—I’d love to hear about it! diff --git a/template-engine/template-engine.markdown b/template-engine/template-engine.markdown index a82215943..a0e049f7e 100644 --- a/template-engine/template-engine.markdown +++ b/template-engine/template-engine.markdown @@ -250,6 +250,7 @@ brace-hashes: In broad strokes, the template engine will have two main phases: _parsing_ the template, and then _rendering_ the template. Rendering the template specifically involves: + * Managing the dynamic context, the source of the data * Executing the logic elements * Implementing dot access and filter execution @@ -767,13 +768,13 @@ this: buffered.append("'hello'") ``` -which will mean that our compiled Python function will have this line: +\noindent which will mean that our compiled Python function will have this line: ```python append_result('hello') ``` -which will add the string `hello` to the rendered output of the template. We have multiple levels of abstraction here which can be difficult to keep straight. The compiler uses \newline `buffered.append("'hello'")`, which creates `append_result('hello')` in the compiled Python function, which when run, appends `hello` to the template result. +\noindent which will add the string `hello` to the rendered output of the template. We have multiple levels of abstraction here which can be difficult to keep straight. The compiler uses \newline `buffered.append("'hello'")`, which creates `append_result('hello')` in the compiled Python function, which when run, appends `hello` to the template result. Back to our Templite class. As we parse control structures, we want to check that they are properly nested. The `ops_stack` list is a stack of strings: @@ -1117,7 +1118,7 @@ expression. Our template expressions can be as simple as a single name: {{user_name}} ``` -or can be a complex sequence of attribute accesses and filters: +\noindent or can be a complex sequence of attribute accesses and filters: ``` {{user.name.localized|upper|escape}} diff --git a/web-server/web-server.markdown b/web-server/web-server.markdown index b4a52d1c7..4f9463202 100644 --- a/web-server/web-server.markdown +++ b/web-server/web-server.markdown @@ -359,7 +359,7 @@ with some formatting placeholders: ''' ``` -and the method that fills this in is: +\noindent and the method that fills this in is: ```python def create_page(self): @@ -606,7 +606,7 @@ class case_always_fail(object): raise ServerException("Unknown object '{0}'".format(handler.path)) ``` -and here's how we construct the list of case handlers +\noindent and here's how we construct the list of case handlers at the top of the `RequestHandler` class: ```python @@ -876,7 +876,7 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): self.wfile.write(content) ``` -while the parent class for our case handlers is: +\noindent while the parent class for our case handlers is: ```python class base_case(object): @@ -901,7 +901,7 @@ class base_case(object): assert False, 'Not implemented.' ``` -and the handler for an existing file +\noindent and the handler for an existing file (just to pick an example at random) is: ```python