100% found this document useful (3 votes)
22 views

Download Complete Functional Data Structures in R: Advanced Statistical Programming in R Thomas Mailund PDF for All Chapters

Structures

Uploaded by

azmiinfraann
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (3 votes)
22 views

Download Complete Functional Data Structures in R: Advanced Statistical Programming in R Thomas Mailund PDF for All Chapters

Structures

Uploaded by

azmiinfraann
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 55

Experience Seamless Full Ebook Downloads for Every Genre at textbookfull.

com

Functional Data Structures in R: Advanced


Statistical Programming in R Thomas Mailund

https://textbookfull.com/product/functional-data-structures-
in-r-advanced-statistical-programming-in-r-thomas-mailund/

OR CLICK BUTTON

DOWNLOAD NOW

Explore and download more ebook at https://textbookfull.com


Recommended digital products (PDF, EPUB, MOBI) that
you can download immediately if you are interested.

Functional Data Structures in R: Advanced Statistical


Programming in R Mailund

https://textbookfull.com/product/functional-data-structures-in-r-
advanced-statistical-programming-in-r-mailund/

textboxfull.com

Functional Programming in R: Advanced Statistical


Programming for Data Science, Analysis and Finance 1st
Edition Thomas Mailund
https://textbookfull.com/product/functional-programming-in-r-advanced-
statistical-programming-for-data-science-analysis-and-finance-1st-
edition-thomas-mailund/
textboxfull.com

Domain Specific Languages in R Advanced Statistical


Programming 1st Edition Thomas Mailund

https://textbookfull.com/product/domain-specific-languages-in-r-
advanced-statistical-programming-1st-edition-thomas-mailund/

textboxfull.com

Domain Specific Languages in R Advanced Statistical


Programming 1st Edition Thomas Mailund

https://textbookfull.com/product/domain-specific-languages-in-r-
advanced-statistical-programming-1st-edition-thomas-mailund-2/

textboxfull.com
Metaprogramming in R: Advanced Statistical Programming for
Data Science, Analysis and Finance 1st Edition Thomas
Mailund
https://textbookfull.com/product/metaprogramming-in-r-advanced-
statistical-programming-for-data-science-analysis-and-finance-1st-
edition-thomas-mailund/
textboxfull.com

Advanced Object-Oriented Programming in R: Statistical


Programming for Data Science, Analysis and Finance 1st
Edition Thomas Mailund
https://textbookfull.com/product/advanced-object-oriented-programming-
in-r-statistical-programming-for-data-science-analysis-and-
finance-1st-edition-thomas-mailund/
textboxfull.com

Beginning Data Science in R: Data Analysis, Visualization,


and Modelling for the Data Scientist 1st Edition Thomas
Mailund
https://textbookfull.com/product/beginning-data-science-in-r-data-
analysis-visualization-and-modelling-for-the-data-scientist-1st-
edition-thomas-mailund/
textboxfull.com

Pointers in C Programming A Modern Approach to Memory


Management Recursive Data Structures Strings and Arrays
Thomas Mailund
https://textbookfull.com/product/pointers-in-c-programming-a-modern-
approach-to-memory-management-recursive-data-structures-strings-and-
arrays-thomas-mailund/
textboxfull.com

Advanced R Statistical Programming and Data Models:


Analysis, Machine Learning, and Visualization 1st Edition
Matt Wiley
https://textbookfull.com/product/advanced-r-statistical-programming-
and-data-models-analysis-machine-learning-and-visualization-1st-
edition-matt-wiley/
textboxfull.com
Functional Data
Structures in R
Advanced Statistical Programming in R

Thomas Mailund
Functional Data
Structures in R
Advanced Statistical
Programming in R

Thomas Mailund
Functional Data Structures in R: Advanced Statistical
Programming in R
Thomas Mailund
Aarhus N, Denmark

ISBN-13 (pbk): 978-1-4842-3143-2 ISBN-13 (electronic): 978-1-4842-3144-9


https://doi.org/10.1007/978-1-4842-3144-9
Library of Congress Control Number: 2017960831

Copyright © 2017 by Thomas Mailund


This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or
part of the material is concerned, specifically the rights of translation, reprinting, reuse of
illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way,
and transmission or information storage and retrieval, electronic adaptation, computer software,
or by similar or dissimilar methodology now known or hereafter developed.
Trademarked names, logos, and images may appear in this book. Rather than use a trademark
symbol with every occurrence of a trademarked name, logo, or image we use the names, logos,
and images only in an editorial fashion and to the benefit of the trademark owner, with no
intention of infringement of the trademark.
The use in this publication of trade names, trademarks, service marks, and similar terms, even if
they are not identified as such, is not to be taken as an expression of opinion as to whether or not
they are subject to proprietary rights.
While the advice and information in this book are believed to be true and accurate at the date of
publication, neither the authors nor the editors nor the publisher can accept any legal
responsibility for any errors or omissions that may be made. The publisher makes no warranty,
express or implied, with respect to the material contained herein.
Cover image by Freepik (www.freepik.com)
Managing Director: Welmoed Spahr
Editorial Director: Todd Green
Acquisitions Editor: Steve Anglin
Development Editor: Matthew Moodie
Technical Reviewer: Karthik Ramasubramanian
Coordinating Editor: Mark Powers
Copy Editor: Corbin P Collins
Distributed to the book trade worldwide by Springer Science+Business Media New York,
233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax (201) 348-4505,
e-mail orders-ny@springer-sbm.com, or visit www.springeronline.com. Apress Media, LLC is a
California LLC and the sole member (owner) is Springer Science + Business Media Finance Inc
(SSBM Finance Inc). SSBM Finance Inc is a Delaware corporation.
For information on translations, please e-mail rights@apress.com, or visit www.apress.com/
rights-permissions.
Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook
versions and licenses are also available for most titles. For more information, reference our Print
and eBook Bulk Sales web page at www.apress.com/bulk-sales.
Any source code or other supplementary material referenced by the author in this book is
available to readers on GitHub via the book's product page, located at www.apress.com/
9781484231432. For more detailed information, please visit www.apress.com/source-code.
Printed on acid-free paper
Table of Contents
About the Author��������������������������������������������������������������������������������vii
About the Technical Reviewer�������������������������������������������������������������ix
Introduction�����������������������������������������������������������������������������������������xi

Chapter 1: Introduction������������������������������������������������������������������������1

Chapter 2: Abstract Data Structures����������������������������������������������������3


Structure on Data��������������������������������������������������������������������������������������������������4
Abstract Data Structures in R�������������������������������������������������������������������������������6
Implementing Concrete Data Structures in R��������������������������������������������������������9
Asymptotic Running Time�����������������������������������������������������������������������������������11
Experimental Evaluation of Algorithms���������������������������������������������������������������15

Chapter 3: Immutable and Persistent Data�����������������������������������������25


Persistent Data Structures����������������������������������������������������������������������������������26
List Functions������������������������������������������������������������������������������������������������������28
Trees�������������������������������������������������������������������������������������������������������������������37
Random Access Lists������������������������������������������������������������������������������������������56

Chapter 4: Bags, Stacks, and Queues�������������������������������������������������67


Bags��������������������������������������������������������������������������������������������������������������������68
Stacks�����������������������������������������������������������������������������������������������������������������73
Queues����������������������������������������������������������������������������������������������������������������74
Side Effects Through Environments��������������������������������������������������������������77
Side Effects Through Closures�����������������������������������������������������������������������79

iii
Table of Contents

A Purely Functional Queue����������������������������������������������������������������������������82


Time Comparisons�����������������������������������������������������������������������������������������84
Amortized Time Complexity and Persistent Data Structures�������������������������85
Double-Ended Queues�����������������������������������������������������������������������������������87
Lazy Queues��������������������������������������������������������������������������������������������������������95
Implementing Lazy Evaluation�����������������������������������������������������������������������96
Lazy Lists�������������������������������������������������������������������������������������������������������98
Amortized Constant Time, Logarithmic Worst-Case, Lazy Queues���������������107
Constant Time Lazy Queues������������������������������������������������������������������������118
Explicit Rebuilding Queue����������������������������������������������������������������������������124

Chapter 5: Heaps������������������������������������������������������������������������������135
Leftist Heaps�����������������������������������������������������������������������������������������������������140
Binomial Heaps�������������������������������������������������������������������������������������������������144
Splay Heaps������������������������������������������������������������������������������������������������������157
Plotting Heaps���������������������������������������������������������������������������������������������������178
Heaps and Sorting���������������������������������������������������������������������������������������������183

Chapter 6: Sets and Search Trees�����������������������������������������������������189


Search Trees�����������������������������������������������������������������������������������������������������190
Red-Black Search Trees������������������������������������������������������������������������������������192
Insertion������������������������������������������������������������������������������������������������������195
Deletion�������������������������������������������������������������������������������������������������������203
Visualizing Red-Black Trees������������������������������������������������������������������������226
Splay Trees��������������������������������������������������������������������������������������������������������231

iv
Table of Contents

Conclusions��������������������������������������������������������������������������������������247
A
 cknowledgements������������������������������������������������������������������������������������������248

Bibliography�������������������������������������������������������������������������������������249

Index�������������������������������������������������������������������������������������������������251

v
About the Author
Thomas Mailund is an associate professor in bioinformatics at Aarhus
University, Denmark. He has a background in math and computer science.
For the last decade, his main focus has been on genetics and evolutionary
studies, particularly comparative genomics, speciation, and gene flow
between emerging species. He has published Beginning Data Science in R,
Functional Programming in R, and Metaprogramming in R with Apress, as
well as other books.

vii
About the Technical Reviewer
Karthik Ramasubramanian works for one
of the largest and fastest-­growing technology
unicorns in India, Hike Messenger, where
he brings the best of business analytics
and data science experience to his role. In
his seven years of research and industry
experience, he has worked on cross-­industry
data science problems in retail, e-commerce,
and technology, developing and prototyping
data-driven solutions. In his previous role at Snapdeal, one of the largest
e-commerce retailers in India, he was leading core statistical modeling
initiatives for customer growth and pricing analytics. Prior to Snapdeal,
he was part of the central database team, managing the data warehouses
for global business applications of Reckitt Benckiser (RB). He has vast
experience working with scalable machine learning solutions for industry,
including sophisticated graph network and self-learning neural networks.
He has a master’s degree in theoretical computer science from PSG College
of Technology, Anna University, and is a certified big data professional. He
is passionate about teaching and mentoring future data scientists through
different online and public forums. He enjoys writing poems in his leisure
time and is an avid traveler.

ix
Introduction
This book gives an introduction to functional data structures. Many
traditional data structures rely on the structures being mutable. We can
update search trees, change links in linked lists, and rearrange values in a
vector. In functional languages, and as a general rule in the R programming
language, data is not mutable. You cannot alter existing data. The
techniques used to modify data structures to give us efficient building
blocks for algorithmic programming cannot be used.
There are workarounds for this. R is not a pure functional language,
and we can change variable-value bindings by modifying environments.
We can exploit this to emulate pointers and implement traditional
data structures this way; or we can abandon pure R programming and
implement data structures in C/C++ with some wrapper code so we can
use them in our R programs. Both solutions allow us to use traditional data
structures, but the former gives us very untraditional R code, and the latter
has no use for those not familiar with other languages than R.
The good news, though, is that we don’t have to reject R when
implementing data structures if we are willing to abandon the traditional
data structures instead. There are data structures that we can manipulate
by building new versions of them rather than modifying them. These data
structures, so-called functional data structures, are different from the
traditional data structures you might know, but they are worth knowing if
you plan to do serious algorithmic programming in a functional language
such as R.
There are not necessarily drop-in replacements for all the data
structures you are used to, at least not with the same runtime performance
for their operations, but there are likely to be implementations for most

xi
Introduction

abstract data structures you regularly use. In cases where you might have
to lose a bit of efficiency by using a functional data structures instead of a
traditional one, however, you have to consider whether the extra speed is
worth the extra time you have to spend implementing a data structure in
exotic R or in an entirely different language.
There is always a trade-off when it comes to speed. How much
programming time is a speed-up worth? If you are programming in R,
chances are you value programmer-time over computer-time. R is a high-­
level language and relatively slow compared to most other languages.
There is a price to providing higher levels of expressiveness. You accept
this when you choose to work with R. You might have to make the same
choice when it comes to selecting a functional data structure over a
traditional one, or you might conclude that you really do need the extra
speed and choose to spend more time programming to save time when
doing an analysis. Only you can make the right choice based on your
situation. You need to find out the available choices to enable you to work
data structures when you cannot modify them.

xii
CHAPTER 1

Introduction
This book gives an introduction to functional data structures. Many
traditional data structures rely on the structures being mutable. We
can update search trees, change links in linked lists, and rearrange
values in a vector. In functional languages, and as a general rule in the R
programming language, data is not mutable. You cannot alter existing data.
The techniques used to modify data structures to give us efficient building
blocks for algorithmic programming cannot be used.
There are workarounds for this. R is not a pure functional language,
and we can change variable-value bindings by modifying environments.
We can exploit this to emulate pointers and implement traditional
data structures this way; or we can abandon pure R programming and
implement data structures in C/C++ with some wrapper code so we can
use them in our R programs. Both solutions allow us to use traditional data
structures, but the former gives us very untraditional R code, and the latter
has no use for those not familiar with other languages than R.
The good news, however, is that we don’t have to reject R when
implementing data structures if we are willing to abandon the traditional
data structures instead. There are data structures we can manipulate by
building new versions of them rather than modifying them. These data
structures, so-called functional data structures, are different from the
traditional data structures you might know, but they are worth knowing if
you plan to do serious algorithmic programming in a functional language
such as R.

© Thomas Mailund 2017 1


T. Mailund, Functional Data Structures in R, https://doi.org/10.1007/978-1-4842-3144-9_1
Chapter 1 Introduction

There are not necessarily drop-in replacements for all the data
structures you are used to, at least not with the same runtime performance
for their operations—but there are likely to be implementations for most
abstract data structures you regularly use. In cases where you might have
to lose a bit of efficiency by using a functional data structure instead of a
traditional one, you have to consider whether the extra speed is worth the
extra time you have to spend implementing a data structure in exotic R or
in an entirely different language.
There is always a trade-off when it comes to speed. How much
programming time is a speed-up worth? If you are programming in R,
the chances are that you value programmer time over computer time. R
is a high-level language that is relatively slow compared to most other
languages. There is a price to providing higher levels of expressiveness.
You accept this when you choose to work with R. You might have to make
the same choice when it comes to selecting a functional data structure
over a traditional one, or you might conclude that you really do need the
extra speed and choose to spend more time programming to save time
when doing an analysis. Only you can make the right choice based on your
situation. You need to find out the available choices to enable you to work
data structures when you cannot modify them.

2
CHAPTER 2

Abstract Data
Structures
Before we get started with the actual data structures, we need to get
some terminologies and notations in place. We need to agree on what an
abstract data structure is—in contrast to a concrete one—and we need to
agree on how to reason with runtime complexity in an abstract way.
If you are at all familiar with algorithms and data structures, you can
skim quickly through this chapter. There won’t be any theory you are not
already familiar with. Do at least skim through it, though, just to make sure
we agree on the notation I will use in the remainder of the book.
If you are not familiar with the material in this chapter, I urge you to
find a text book on algorithms and read it. The material I cover in this
chapter should suffice for the theory we will need in this book, but there
is a lot more to data structures and complexity than I can possibly cover
in a single chapter. Most good textbooks on algorithms will teach you a lot
more, so if this book is of interest, you should not find any difficulties in
continuing your studies.

© Thomas Mailund 2017 3


T. Mailund, Functional Data Structures in R, https://doi.org/10.1007/978-1-4842-3144-9_2
Chapter 2 Abstract Data Structures

Structure on Data
As the name implies, data structures have something to do with structured
data. By data, we can just think of elements from some arbitrary set. There
might be some more structure to the data than the individual data points,
and when there is we keep that in mind and will probably want to exploit
that somehow. However, in the most general terms, we just have some
large set of data points.
So, a simple example of working with data would be imagining we
have this set of possible values—say, all possible names of students at a
university—and I am interested in a subset—for example, the students
that are taking one of my classes. A class would be a subset of students,
and I could represent it as the subset of student names. When I get an
email from a student, I might be interested in figuring out if it is from one
of my students, and in that case, in which class. So, already we have some
structure on the data. Different classes are different subsets of student
names. We also have an operation we would like to be able to perform on
these classes: checking membership.
There might be some inherent structure to the data we work with, which
could be properties such as lexicographical orders on names—it enables us to
sort student names, for example. Other structure we add on top of this. We add
structure by defining classes as subsets of student names. There is even a third
level of structure: how we represent the classes on our computer.
The first level of structure—inherent in the data we work with—is not
something we have much control over. We might be able to exploit it in
various ways, but otherwise, it is just there. When it comes to designing
algorithms and data structures, this structure is often simple information;
if there is order in our data, we can sort it, for example. Different
algorithms and different data structures make various assumptions about
the underlying data, but most general algorithms and data structures make
few assumptions. When I make assumptions in this book, I will make those
assumptions explicit.

4
Chapter 2 Abstract Data Structures

The second level of structure—the structure we add on top of the


universe of possible data points—is information in addition to what just
exists out there in the wild; this can be something as simple as defining
classes as subsets of student names. It is structure we add to data for
a purpose, of course. We want to manipulate this structure and use it
to answer questions while we evaluate our programs. When it comes
to algorithmic theory, what we are mainly interested in at this level is
which operations are possible on the data. If we represent classes as sets
of student names, we are interested in testing membership to a set. To
construct the classes, we might also want to be able to add elements to an
existing set. That might be all we are interested in, or we might also want to
be able to remove elements from a set, get the intersection or union of two
sets, or do any other operation on sets.
What we can do with data in a program is largely defined by the
operations we can do on structured data; how we implement the
operations is less important. That might affect the efficiency of the
operations and thus the program, but when it comes to what is possible to
program and what is not—or what is easy to program and what is hard, at
least—it is the possible operations that are important.
Because it is the operations we can do on data, and now how we
represent the data—the third level of structure we have—that is most
important, we distinguish between the possible operations and how they
are implemented. We define abstract data structures by the operations
we can do and call different implementations of them concrete data
structures. Abstract data structures are defined by which operations we can
do on data; concrete data structures, by how we represent the data and
implement these operations.

5
Chapter 2 Abstract Data Structures

Abstract Data Structures in R


If we define abstract data structures by the operations they provide, it is
natural to represent them in R by a set of generic functions. In this book,
I will use the S3 object system for this.1
Let’s say we want a data structure that represents sets, and we need
two operations on it: we want to be able to insert elements into the set, and
we want to be able to check if an element is found in the set. The generic
interface for such a data structure could look like this:

insert <- function(set, elem) UseMethod("insert")


member <- function(set, elem) UseMethod("member")

Using generic functions, we can replace one implementation with


another with little hassle. We just need one place to specify which
concrete implementation we will use for an object we will otherwise only
access through the abstract interface. Each implementation we write will
have one function for constructing an empty data structure. This empty
structure sets the class for the concrete implementation, and from here on
we can access the data structure through generic functions. We can write a
simple list-based implementation of the set data structure like this:

empty_list_set <- function() {


  structure(c(), class = "list_set")
}

insert.list_set <- function(set, elem) {


  structure(c(elem, set), class = "list_set")
}

1
I f you are unfamiliar with generic functions and the S3 system, you can check out
my book Advanced Object-Oriented Programming in R book (Apress, 2017), where
I explain all this.

6
Chapter 2 Abstract Data Structures

member.list_set <- function(set, elem) {


  elem %in% set
}

The empty_list_set function is how we create our first set of the


concrete type. When we insert elements into a set, we also get the right
type back, but we shouldn’t call insert.list_set directly. We should
just use insert and let the generic function mechanism pick the right
implementation. If we make sure to make the only point where we refer
to the concrete implementation be the creation of the empty set, then we
make it easier to replace one implementation with another:

s <- empty_list_set()
member(s, 1)
## [1] FALSE
s <- insert(s, 1)
member(s, 1)
## [1] TRUE

When we implement data structures in R, there are a few rules of


thumb we should follow, and some are more important than others.
Using a single “empty data structure” constructor and otherwise generic
interfaces is one such rule. It isn’t essential, but it does make it easier to
work with abstract interfaces.
More important is this rule: keep modifying and querying a data
structure as separate functions. Take an operation such as popping the
top element of a stack. You might think of this as a function that removes
the first element of a stack and then returns the element to you. There
is nothing wrong with accessing a stack this way in most languages, but
in functional languages, it is much better to split this into two different
operations: one for getting the top element and another for removing it
from the stack.

7
Chapter 2 Abstract Data Structures

The reason for this is simple: our functions can’t have side effects. If a
“pop” function takes a stack as an argument, it cannot modify this stack. It
can give you the top element of the stack, and it can give you a new stack
where the top element is removed, but it cannot give you the top element
and then modify the stack as a side effect. Whenever we want to modify
a data structure, what we have to do in a functional language, is to create
a new structure instead. And we need to return this new structure to the
caller. Instead of wrapping query answers and new (or “modified”) data
structures in lists so we can return multiple values, it is much easier to
keep the two operations separate.
Another rule of thumb for interfaces that I will stick to in this book,
with one exception, is that I will always have my functions take the data
structure as the first argument. This isn’t something absolutely necessary,
but it fits the convention for generic functions, so it makes it easier to work
with abstract interfaces, and even when a function is not abstract—when
I need some helper functions—remembering that the first argument is
always the data structure is easier. The one exception to this rule is the
construction of linked lists, where tradition is to have a construction
function, cons, that takes an element as its first argument and a list as its
second argument and construct a new list where the element is put at the
head of the list. This construction is too much of a tradition for me to mess
with, and I won’t write a generic function of it, so it doesn’t come into
conflict with how we handle polymorphism.
Other than that, there isn’t much more language mechanics to creating
abstract data structures. All operations we define on an abstract data
structure have some intended semantics to them, but we cannot enforce
this through the language; we just have to make sure that the operations
we implement actually do what they are supposed to do.

8
Chapter 2 Abstract Data Structures

Implementing Concrete Data Structures in R


When it comes to concrete implementations of data structures, there
are a few techniques we need in order to translate the data structure
designs into R code. In particular, we need to be able to represent what
are essentially pointers, and we need to be able to represent empty
data structures. Different programming languages will have different
approaches to these two issues. Some allow the definition of recursive data
types that naturally handle empty data structures and pointers, others have
unique values that always represent “empty,” and some have static type
systems to help. We are programming in R, though, so we have to make it
work here.
For efficient data structures in functional programming, we need
recursive data types, which essentially boils down to representing pointers.
R doesn’t have pointers, so we need a workaround. That workaround is
using lists to define data structures and using named elements in lists as
our pointers.
Consider one of the simplest data structures known to man: the linked
list. If you are not familiar with linked lists, you can read about them in the
next chapter, where I consider them in some detail. In short, linked lists
consist of a head—an element we store in the list—and a tail—another list,
one item shorter. It is a recursive definition that we can write like this:

LIST = EMPTY | CONS(HEAD, LIST)

Here EMPTY is a special symbol representing the empty list, and


CONS—a traditional name for this, from the Lisp programming language—a
symbol that constructs a list from a HEAD element and a tail that is another
LIST. The definition is recursive—it defines LIST in terms of a tail that
is also a LIST—and this in principle allows lists to be infinitely long. In
practice, a list will eventually end up at EMPTY.

9
Chapter 2 Abstract Data Structures

We can construct linked lists in R using R’s built-in list data structure.
That structure is not a linked list; it is a fixed-size collection of elements
that are possibly named. We exploit named elements to build pointers. We
can implement the CONS construction like this:

linked_list_cons <- function(head, tail) {


  structure(list(head = head, tail = tail),
            class = "linked_list_set")
}

We just construct a list with two elements, head and tail. These will
be references to other objects—head to the element we store in the list, and
tail to the rest of the list—so we are in effect using them as pointers. We
then add a class to the list to make linked lists work as an implementation
of an abstract data structure.
Using classes and generic functions to implement polymorphic
abstract data structures leads us to the second issue we need to deal with
in R. We need to be able to represent empty lists. The natural choice for
an empty list would be NULL, which represents “nothing” for the built-in
list objects, but we can’t get polymorphism to work with NULL. We can’t
give NULL a class. We could, of course, still work with NULL as the empty list
and just have classes for non-empty lists, but this clashes with our desire
to have the empty data structures being the one point where we decide
concrete data structures instead of just accessing them through an abstract
interface. If we didn’t give empty data structures a type, we would need
to use concrete update functions instead. That could make switching
between different implementations cumbersome. We really do want to
have empty data structures with classes.
The trick is to use a sentinel object to represent empty structures.
Sentinel objects have the same structure as non-empty data structure
objects—which has the added benefit of making some implementations
easier to write—but they are recognized as representing “empty.” We
construct a sentinel as we would any other object, but we remember it

10
Chapter 2 Abstract Data Structures

for future reference. When we create an empty data structure, we always


return the same sentinel object, and we have a function for checking
emptiness that examines whether its input is identical to the sentinel
object. For linked lists, this sentinel trick would look like this:

linked_list_nil <- linked_list_cons(NA, NULL)


empty_linked_list_set <- function() linked_list_nil
is_empty.linked_list_set <- function(x)
  identical(x, linked_list_nil)

The is_empty function is a generic function that we will use for all data
structures.
The identical test isn’t perfect. It will consider any list element
containing NA as the last item in a list as the sentinel. Because we don’t
expect anyone to store NA in a linked list—it makes sense to have missing
data in a lot of analysis, but rarely does it make sense to store it in data
structures—it will have to do.
Using a sentinel for empty data structures can also occasionally be
useful for more than dispatching on generic functions. Sometimes, we
actually want to use sentinels as proper objects, because it simplifies certain
functions. In those cases, we can end up with associating meta-­data with
“empty” sentinel objects. We will see examples of this when we implement
red-black search trees. If we do this, then checking for emptiness
using identical will not work. If we modify a sentinel to change meta-
information, it will no longer be identical to the reference empty object. In
those cases, we will use other approaches to testing for emptiness.

Asymptotic Running Time


Although the operations we define in the interface of an abstract data
type determine how we can use these in our programs, the efficiency of
our programs depends on how efficient the data structure operations are.

11
Chapter 2 Abstract Data Structures

Because of this, we often consider the time efficiency part of the interface
of a data structure—if not part of the abstract data structure, we very much
care about it when we have to pick concrete implementations of data
structures for our algorithms.
When it comes to algorithmic performance, the end goal is always to
reduce wall time—the actual time we have to wait for a program to finish.
But this depends on many factors that cannot necessarily know about
when we design our algorithms. The computer the code will run on might
not be available to us when we develop our software, and both its memory
and CPU capabilities are likely to affect the running time significantly. The
running time is also likely to depend intimately on the data we will run the
algorithm on. If we want to know exactly how long it will take to analyze a
particular set of data, we have to run the algorithm on this data. Once we
have done this, we know exactly how long it took to analyze the data, but
by then it is too late to explore different solutions to do the analysis faster.
Because we cannot practically evaluate the efficiency of our algorithms
and data structures by measuring the running time on the actual data we
want to analyze, we use different techniques to judge the quality of various
possible solutions to our problems.
One such technique is the use of asymptotic complexity, also known as
big-O notation. Simply put, we abstract away some details of the running
time of different algorithms or data structure operations and classify their
runtime complexity according to upper bounds known up to a constant.
First, we reduce our data to its size. We might have a set with n
elements, or a string of length n. Although our data structures and
algorithms might use very different actual wall time to work on different
data of the same size, we care only about the number n and not the details
of the data. Of course, data of the same size is not all equal, so when
we reduce all our information about it to a single size, we have to be a
little careful about what we mean when we talk about the algorithmic
complexity of a problem. Here, we usually use one of two approaches: we
speak of the worst-case or the average/expected complexity. The worst-case

12
Chapter 2 Abstract Data Structures

runtime complexity of an algorithm is the longest running time we can


expect from it on any data of size n. The expected runtime complexity of
an algorithm is the mean running time for data of size n, assuming some
distribution over the possible data.
Second, we do not consider the actual running time for data of size
n—where we would need to know exactly how many operations of
different kinds would be executed by an algorithm, and how long each
kind of operation takes to execute. We just count the number of operations
and consider them equal. This gives us some function of n that tells us how
many operations an algorithm or operation will execute, but not how long
each operation takes. We don’t care about the details when comparing
most algorithms because we only care about asymptotic behavior when
doing most of our algorithmic analysis.
By asymptotic behavior, I mean the behavior of functions when
the input numbers grow large. A function f (n) is an asymptotic upper
bound for another function g(n) if there exists some number N such
that g(n) ≤ f (n) whenever n > N. We write this in big-O notation as
g(n) ∈ O( f (n)) or g(n) = O( f (n)) (the choice of notation is a little arbitrary
and depends on which textbook or reference you use).
The rationale behind using asymptotic complexity is that we can use
it to reason about how algorithms will perform when we give them larger
data sets. If we need to process data with millions of data points, we might
be about to get a feeling for their running time through experiments
with tens or hundreds of data points, and we might conclude that one
algorithm outperforms another in this range. But that does not necessarily
reflect how the two algorithms will compare for much larger data. If
one algorithm is asymptotically faster than another, it will eventually
outperform the other—we just have to get to the point where n gets large
enough.
A third abstraction we often use is to not be too concerned with getting
the exact number of operations as a function of n correct. We just want
an upper bound. The big-O notation allows us to say that an algorithm

13
Chapter 2 Abstract Data Structures

runs in any big-O complexity that is an upper bound for the actual
runtime complexity. We want to get this upper bound as exact as we can,
to properly evaluate different choices of algorithms, but if we have upper
and lower bounds for various algorithms, we can still compare them.
Even if the bounds are not tight, if we can see that the upper bound of one
algorithm is better than the lower bound of another, we can reason about
the asymptotic running time of solutions based on the two.
To see the asymptotic reasoning in action, consider the set
implementation we wrote earlier:

empty_list_set <- function() {


  structure(c(), class = "list_set")
}

insert.list_set <- function(set, elem) {


  structure(c(elem, set), class = "list_set")
}

member.list_set <- function(set, elem) {


  elem %in% set
}

It represents the set as a vector, and when we add elements to the


set, we simply concatenate the new element to the front of the existing
set. Vectors, in R, are represented as contiguous memory, so when we
construct new vectors this way, we need to allocate a block of memory to
contain the new vector, copy the first element into the first position, and
then copy the entire old vector into the remaining positions of the new
vector. Inserting an element into a set of size n, with this implementation,
will take time O(n)—we need to insert n+1 set elements into newly
allocated blocks of memory. Growing a set from size 0 to size n by
repeatedly inserting elements will take time O(n2).
The membership test, elem %in% set, runs through the vector until it
either sees the value elem or reaches the end of the vector. The best case

14
Chapter 2 Abstract Data Structures

would be to see elem at the beginning of the vector, but if we consider


worst-case complexity, this is another O(n) runtime operation.
As an alternative implementation, consider linked lists. We insert
elements in the list using the cons operation, and we check membership
by comparing elem with the head of the list. If the two are equal, the set
contains the element. If not, we check whether elem is found in the rest
of the list. In a pure functional language, we would use recursion for this
search, but here I have just implemented it using a while loop:

insert.linked_list_set <- function(set, elem) {


  linked_list_cons(elem, set)
}

member.linked_list_set <- function(set, elem) {


  while (!is_empty(set)) {
    if (set$head == elem) return(TRUE)
    set <- set$tail
  }
  return(FALSE)
}

The insert operation in this implementation takes constant time. We


create a new list node and set the head and tail in it, but unlike the vector
implementation, we do not copy anything. For the linked list, inserting
elements is an O(1) operation. The membership check, though, still runs
in O(n) because we still do a linear search.

Experimental Evaluation of Algorithms


Analyzing the asymptotic performance of algorithms and data structures
is the only practical approach to designing programs that work on very
large data, but it cannot stand alone when it comes to writing efficient
code. Some experimental validation is also needed. We should always

15
Chapter 2 Abstract Data Structures

perform experiments with implementations to 1) be informed about the


performance constants hidden beneath the big-O notation, and 2) to
validate that the performance is as we expect it to be.
For the first point, remember that just because two algorithms are in
the same big-O category—say, both are in O(n2)—that doesn’t mean they
have the same wall-time performance. It means that both algorithms are
asymptotically bounded by some function c⋅n2 where c is a constant. Even
if both are running in quadratic time, so that the upper bound is actually
tight, they could be bounded by functions with very different constants.
They may have the same asymptotic complexity, but in practice, one could
be much faster than the other. By experimenting with the algorithms, we
can get a feeling, at least, for how the algorithms perform in practice.
Experimentation also helps us when we have analyzed the worst case
asymptotic performance of algorithms, but where the data we actually
want to process is different from the worst possible data. If we can create
samples of data that resemble the actual data we want to analyze, we can
get a feeling for how close it is to the worst case, and perhaps find that an
algorithm with worse worst case performance actually has better average
case performance.
As for point number two for why we want to experiment with
algorithms, it is very easy to write code with a different runtime
complexity than we expected, either because of simple bugs or because
we are programming in R, a very high-level language, where language
constructions potentially hide complex operations. Assigning to a vector,
for example, is not a simple constant time operation if more than one
variable refers to the vector. Assignment to vector elements potentially
involves copying the entire vector. Sometimes it is a constant time
operation; sometimes it is a linear time operation. We can deduce what
it will be by carefully reading the code, but it is human to err, so it makes
sense always to validate that we have the expected complexity by running
experiments.

16
Chapter 2 Abstract Data Structures

In this book, I will use the microbenchmark package to run


performance experiments. This package lets us run a number of
executions of the same operation and get the time it takes back in
nanoseconds. I don’t need that fine a resolution, but it is nice to be able to
get a list of time measurements. I collect the results in a tibble data frame
from which I can summarize the results and plot them later. The code I use
for my experiments is as follows:

library(tibble)
library(microbenchmark)

get_performance_n <- function(


  algo
  , n
  , setup
  , evaluate
  , times
  , ...) {

  config <- setup(n)


  benchmarks <- microbenchmark(evaluate(n, config),
                               times = times)
  tibble(algo = algo, n = n,
         time = benchmarks$time / 1e9) # time in sec
}

get_performance <- function(


  algo
  , ns
  , setup
  , evaluate
  , times = 10
  , ...) {

17
Chapter 2 Abstract Data Structures

  f <- function(n)


    get_performance_n(algo, n, setup, evaluate,
                      times = times, ...)
  results <- Map(f, ns)
  do.call('rbind', results)
}

The performance experiment functions let me specify a function for


setting up an experiment and another for running the experiment. If I want
to evaluate the time it takes to construct a set of the numbers from one up
to n, I can use the setup function to choose the implementation—based
on their respective empty structures—and I can construct the sets in the
evaluate function:

setup <- function(empty) function(n) empty


evaluate <- function(n, empty) {
  set <- empty
  elements <- sample(1:n)
  for (elm in elements) {
    set <- insert(set, elm)
  }
}

ns <- seq(1000, 5000, by = 500)


performance <- rbind(
  get_performance("list()", ns,
                  setup(empty_list_set()), evaluate),
  get_performance("linked list", ns,
                  setup(empty_linked_list_set()), evaluate)
)

I permute the elements I insert in the sets to avoid any systematic


bias in how the data is added to the sets. There isn’t any with the two
implementations we have here, but for many data structures there are, so

18
Chapter 2 Abstract Data Structures

this is a way of getting an average case complexity instead of a best-case or


worst-case performance.
Running the performance measuring code with these two functions
and the two set implementations, I get the results I have plotted in
Figure 2-1:

library(ggplot2)
ggplot(performance, aes(x = n, y = time, colour = algo)) +
  geom_jitter() +
  geom_smooth(method = "loess",
              span = 2, se = FALSE) +
  scale_colour_grey("Data structure", end = 0.5) +
  xlab(quote(n)) + ylab("Time (sec)") + theme_minimal()

In this figure, we can see what we expected from the asymptotic


runtime analysis. The two approaches are not that different for small sets,
but as the size of the data grows, the list implementation takes relatively
longer to construct a set than the linked list implementation.

Figure 2-1. Direct comparison of the two set construction implementations

19
Other documents randomly have
different content
William preached to us only yesterday about the poor man
that fell among the thieves."

"Oh, Sir William, Sir William," returned the woman,


scornfully. "Sir William had better look out for himself. He is
an arrant Gospeller and Lutheran, unless he is much
miscalled; and we all know what that comes to. Just as you
like, but you are a fool for your pains. The next time your
children want bread, don't come to me, that's all."

"I am not likely to do so, since the only time I ever asked
you for anything you gave me a flat refusal," said Mary
Brent. "I trust my children will never be the poorer for my
kindness to this poor lad, but if they are, I can't help it."

"And if they should be, you have enough of warm friends


who will not let them or you want, my good Mary," said
Jack, who had stood quietly listening to this conversation.

Dame Higgins started violently, as did Mary herself, for in


the heat of discussion and the gathering twilight, they had
not noticed Jack's approach.

"Is that you, Master Jack? I am right glad to see you," said
Mary. "I felt sure you would come, or I should not have
been so bold as to send."

"You did quite right," said Jack. "The folks at home have
sent some delicacies for the sick man, and also something
for your own table. Let me carry it in for you, the basket is
heavy."

"Good lack, so it is," said Dame Higgins, casting an envious


eye on the contents of the basket, as Jack lifted the clean
white cloth which covered it. "What luck some folks have, to
be sure! Such baskets never come to our house."
"But I thought your motto was that every herring should
hang by its own head," said Jack, as he entered the house.

Dame Higgins only replied by a prodigious sniff, and some


remarks; apparently spoken to the air, concerning folk who
knew which side their bread was buttered, and how to turn
their charities to good account.

"Dame Higgins is out of humor," observed Jack.

"She is seldom anything else, save when she has made an


uncommonly good bargain, or some unexpected gain hath
come to her," replied Mary Brent. "She and her husband
seem to care for nothing but saving and making money. I
have been poor enough, as you know; but I never saw the
day I would exchange lots with Joan Higgins; with all her
wealth, she is poorer this day, than ever I was in my worst
times."

"She would always be poor, if she had the revenues of the


cardinal himself," said Jack. "But what is this about your
lodger?"

"Oh, poor young man, he is in a sad case enough," replied


Mary Brent. "They found him floating on a kind of raft
pinned together with bits of wreck, and took him off. He
says the ship foundered, as nearly as he can tell, about
twelve days before he was rescued, that there were two
men and a young boy on the raft with him at first, but they
died one after the other, till there was no one left but
himself. He is a well-made but slender youth, and does not
look like a regular sailor; indeed, I think he hath all the air
of a gentleman born; but he is not willing to give any
account of himself, and there is no use in teasing him till he
gets stronger. He wanders a deal at times, but more from
weakness than from fever, I think, and then his talk is
always about Holford; and Davy and I thought that as you
had been so long at Holford of late, you might perhaps find
out something from him. He may have friends, perhaps a
mother who is wearying for news of him."

"I will see what I can do," said Jack. "I had almost forgot to
wish you joy of Davy's return. I hear he has done very
well."

"Yes, indeed, Davy is second mate, which is great


promotion for one so young," said Mary. "He earns good
wages besides what he can make by trading on his own
account, and he has brought me home a good sum of
money, besides presents of foreign stuffs far too fine for me
to wear, and many curious outlandish toys for the children. I
know you will be glad to hear as much, for your folks have
always taken his part," added Mary, wiping the glad and
proud tears from her eyes; "but thank God, nobody can call
my Davy a scapegrace any more."

"He is a brave good lad, and I always thought so," said


Jack, "and to my mind has shown himself a far better
Christian by going to work to help you and the children than
he would have done by becoming a monk and leaving you
to shift for yourselves or live on charity. But it grows late.
Shall I go up and see this stranger?"

"If you will," said Mary. "He lies in my best room."

The stranger was as Mary had described him, a dark slender


young man, sunburnt and emaciated, yet having the air of a
gentleman. He was comfortably accommodated in his
hostess's best bed, and Mary had combed his dark curling
locks and trimmed his beard, evidently wishing to set him
off to the best advantage.
The moment Jack's eyes fell upon him, he was puzzled by a
resemblance to some very familiar face, but whose he could
not tell.

"If I have never seen you before, I have certainly seen


somebody very like you," was his first thought.

"See here, Master Paul," said Mary in a tone which was both
affectionate and respectful. "Here is young Master Lucas
come to see you."

"He is very kind," said the invalid faintly smiling. "I am no


great sight, I am afraid, but any friend of yours is welcome,
my kind nurse."

"I did not come to stare at you, but to see what I could do
for you," said Jack, seating himself by the bed. "My father
has sent you some nourishing food, and bid me ask what
else we could do for you. You seem very ill and weak."

"I have gained a little, I think, since I came here," said the
invalid. "It is such a wonderful blessing to be among kindly
English folk once more and to lie still in a clean and decent
bed."

"I am sure you are heartily welcome," said Mary Brent. "But
I will leave Master Jack to sit by you if he will be so kind, for
I have matters to attend to below stairs."

Mary went away, and Jack remained quietly sitting by the


side of the invalid, who seemed to have fallen into a doze.
The more Jack looked at him, the more certain he became
that he had seen him or some one like him before.

Presently the stranger opened his eyes and asked for drink.
Jack supplied his wants and arranged his pillow comfortably.
"Do you live in this place?" asked the stranger whom Mary
Brent had called Paul. "You do not look like a town-bred
lad."

"I am so nevertheless," replied Jack; "but I have been


keeping sheep all the summer with my good uncle at
Holford."

"At Holford!" repeated Paul with a little start.

"Yes, my uncle is shepherd to the good knight of Holford."

"What, old Thomas Sprat! Is he alive still?" asked Paul with


interest.

"He is alive and well," said Jack more puzzled than ever.
"Do you then know my uncle and the family at the Hall?"

"Yes—that is, I was once in the family of Sir John for a


time," said Paul with evident embarrassment. "Is the good
knight well?"

"He is well, or was so last week," said Jack. "I saw him in
the market-place a few days since. He hath grown very gray
of late years, but still holds his own."

Paul sighed. "And my—I would say, my lady—have you ever


seen her?"

"Oh, yes, often while I was at Holford," replied Jack. "She


goes about among the poor people a great deal, but rarely
visits among the gentry since her son's death."

"She believes him dead then," murmured the stranger so


low that Jack could but just catch the words.
He answered them as if they had been addressed to him
quietly, but with his heart beating fast as a wild idea
occurred to his mind.

"My lady thinks him dead, and has caused many masses to
be sung for him; but the knight will not believe it. They say
he keeps his son's room in the same order in which the poor
young gentleman left it, when he went to college, and he
will not suffer his son's old dog to be killed, though the poor
old beast can hardly crawl from the hearth to the hall door.
I have often marvelled much how the young master could
leave such a kind father."

"Because he was a fool," said Paul vehemently, "a gull, a


thrice-sodden ass; an ape who must needs mimic what
others did, and ruffle it in silk and gold with the sons of
court favorites and noblemen."

"Then you knew the heir of Holford?" said Jack, his first idea
growing stronger the more he heard.

"Yes—that is, I knew him at college," replied Paul, making


an evident effort to control his agitation. "He was a foolish
boy, and unworthy of so good a home."

"I have heard that the men blamed the knight for having
been over-strict with him, and that the young gentleman
himself laid his wrong-doing to the same cause."

"That is not true," said Paul almost fiercely. "He never sunk
so low as that. He would have been the basest hound that
ever lived, had he done so."

"I am glad to hear that," said Jack. "I can never think much
of those who strive to excuse themselves by laying all their
faults on the shoulders of others. I wish he would come
back to his home. I am sure the good knight would receive
him joyfully—even as the prodigal in the parable was
received by his father. But you are talking too much for one
in your weak state," he added. "Let me give you some food
or a cordial, and then do you try to sleep."

"I am indeed weary," said Paul. "But must you go away?"

"Not if you need me," replied Jack. "I will stay all night if
you desire it. I can easily send word to my father, and I am
sure he will make no objection to my doing so."

Paul said something about it being too much to ask of a


stranger, but he was so evidently pleased by the
proposition, that Jack at once decided to stay, and went
down-stairs to seek a messenger.

Davy willingly undertook the office.

"And what do you make of him?" he asked.

"Very little as yet," replied Jack, unwilling to mention his


suspicions. "He has been at Holford at some time, I dare
say in the train of some gentleman who came to visit at the
Hall—and his mind runs on it. He is very feeble, and his
mind is disturbed, but he seems to like to have me beside
him."

"I am sure you are very kind," said Davy. "What shall I say
to your father for you?"

"Only that I am going to watch by the stranger's bedside,


with his good leave," said Jack; "and you may, if you
please, ask Dame Cicely for my warm doublet, for the
nights are growing chill."

"And that reminds me that I may as well kindle a little fire


in the room," said Mary. "A blaze is a cheerful companion,
and, as you say, the nights are growing chill. But, Master
Jack," said she, detaining Jack for a moment, after Davy
had gone on his errand, "I want to consult you about a
certain matter, and that is, as to whether I should send for
a priest?"

"Has the young man asked for a priest?" inquired Jack.

"No, that he has not," replied Mary. "He shook his head
when I asked him at his first coming whether he would have
one; and when I did but hint at it again, he said right
sharply, 'No, no! No priest,' and Davy bid me not trouble
him about the matter. But maybe he should have one for all
that."

"I would not trouble him at present," said Jack. "If he grows
worse we can send for Sir William, who will come any hour
of the night, you know."

"That he will, the good man," returned Mary. "Better man


never lived, for all they call him a Lutheran. But you know,
Master Jack, my poor husband died without the sacraments,
and I would ill like to have such a thing happen again in my
house."

Jack quieted the good woman with renewed assurances that


he would send for Sir William if it became necessary. He
reminded her that the stranger was very weak, and it was
not worth while to oppose his wishes when a little thing
might set him back and perhaps throw him into a fever.

"I dare say you are right," said Mary. "I will get you some
supper and make ready a comfortable morsel to eat during
the night, for you must take care of your own health, you
know, and you have been delicate of late."
Jack was too much excited with the discovery he supposed
himself to have made, to feel hungry; but he was one who
could put his own feelings aside for the sake of other
people. He consented to eat some supper to satisfy Mary's
hospitable thought, and found, as young people are apt to,
that he was hungry enough to do full justice to the savory
fare she had provided.

He then stole back to the sick man's chamber, where a


cheerful little fire was already burning, while a pile of wood
and fagots offered the means of replenishing it during the
night. Mary Brent moved about gently putting matters in
order, and covering a little table in one corner with
refreshments for the watcher as well as the invalid. Finally
she beckoned Jack aside and, with rather a mysterious air,
opened a little cupboard, hidden by a piece of tapestry:

"Here are some books which belonged to my poor


husband," said she. "I found them when I was putting the
house to rights, and hid them away that the children might
not see them, for I cannot read, and know not whether they
be good books or no. But I dare say they will not hurt you,
and they may serve to help you keep awake."

Jack looked over the books, which were partly written and
partly printed. They formed an odd collection of Canterbury
tales, lives of saints, and one or two old romances. He
turned them over and at last discovered, hidden under the
disguise of a volume of ballads, a manuscript book carefully
written out. He took it to the fire to examine it, and read on
the title—

"This boke ys the boke of the prophet Isiach,


written out by me from a boke of the Scripture
which a man had in Antwerp, and ys doubtless
ye trew word of ye livinge God."

Underneath was written in the same hand—

"O Lord, how long."

Jack was overjoyed at the discovery. He had never seen any


part of the Old Testament except the Psalms, and could
hardly believe in his good fortune. He looked the books over
once more, and found a part of St. John's Gospel, evidently
copied by the same hand.

Both books had been carefully studied, as was evident from


the marks and marginal notes they contained. Jack
understood at once the secret of David Brent's refusal to
see the priest, and his dying, as his wife said, without the
sacraments, yet as peaceful and calm as a babe. He felt, as
he looked at the books written out with so much care by a
hand evidently unused to holding a pen, like one who
comes unexpectedly on the writing of a dear friend long
dead; and he vowed that as long as he lived, David Brent's
children should never want anything that he could do for
them.

He trimmed the shaded lamp and sat down to read, but


even the interest of his new discovery could not divert his
attention from the sick man. Was he really Sir John
Brydges's long-lost son? And if so, what was to be done to
restore him to his parents? Could he be persuaded to return
to his father's house? That would be best for all.

"But if he will not, the knight must come to him," Jack said
to himself. "I must bring the father and son face to face,
and then I am sure all will be well. I remember what the
knight said on the terrace at Holford the day I went to
speak with Master Fleming. Oh, how I wish he were here.
But there is no use in speculating. I must wait and see how
matters will turn out."

Jack once more addressed himself to his book, and read till
he was aroused by the voice of the invalid. He rose and
went to the bedside. Paul had been sleeping quietly for
some time, but he now began to talk, though without
opening his eyes; and Jack perceived that he was
wandering, between sleeping and waking. He held his
breath not to lose a word.

"Mother, mother," murmured the sick man. "Mother, I am


not dead. I need no masses, even if they were worth
anything. Only take me home, and lay me on my own bed,
and let my father sit by me as he used to do in old times.
Father will forgive me for disgracing him when he knows I
am sorry for what I have done. 'While he was yet a great
way off, his father saw him.' Master Firth bade me return to
my father and seek his forgiveness. But a heretic!"

Jack started and drew nearer to the bedside.

"A heretic!" repeated Paul.

And then looking up, and seeing Jack bending over him, he
added eagerly, but yet with a certain wildness which showed
his mind was still wandering—"You have seen my father of
late. Do you think he would receive and forgive me if he
knew that I had heard the Lutherans preach—that I was of
the new religion?"

"I am sure he would—quite sure," said Jack. "Some men


say he is a favorer of the new religion himself."
"But my mother—what would she say? She is a proud and
devout lady, you know."

"She is your mother," said Jack briefly, as though that were


enough.

"But if she should refuse me when she knows the truth, if


she should turn her back upon me after all, it would go near
to break my heart. And you know I must needs speak the
truth. 'He that loveth father or mother more than Me, is not
worthy of Me.'"

Jack saw that his patient was becoming over-excited, and


was likely to do himself harm.

"Hush!" said he, with kindly authority. "You will do yourself


a mischief by talking so much, and I am sure your mother
will not be pleased with that. Let me give you some
refreshment, and then I will read to you, and you must try
to sleep."

"But what will you read? Will you read from the Scriptures?"
asked Paul, looking eagerly into Jack's face. "But no, you
must not do so, or they will put you in prison and on the
rack as they did me. See here," and he pushed up his
sleeves and showed his emaciated wrists covered with
terrible scars, the sight of which made Jack's blood boil and
his fingers clinch involuntarily. "You must not read the
Scripture, and besides you do not know it."

"I do both know the Scripture, and will read it to you,


dearest brother," said Jack, striving to speak calmly, though
he was thrilling all over with excitement. "Do but lie down
and be quiet, and I will read as much as you will."

"But are you then a Lutheran?" asked Paul, looking wistfully


into his face, "Or are you laying a trap for me, as they did in
Flanders? There be no Lutherans in England."

"May God so deal with me as I am dealing truly with you,"


said Jack, solemnly. "There are some—yes many, in this
place—who love the Gospel and read it, but as yet secretly,
for fear of the oppressors. Have no fear, but down and rest,
and I will read the holy Scripture to you as long as you will."

Seemingly reassured, Paul lay down, and Jack began


reading from the book he had discovered. There was much
of course that he did not in the least understand, but he
found enough which was plain to make him long for more.

Paul now and then spoke a few words, but more and more
dreamily, and Jack had at last the satisfaction of seeing him
fall into a sound quiet sleep. He sat reading and thinking by
the bedside till the gray dawn began to steal in at the
window. As he rose to replenish the fire, Paul was roused
and opened his eyes.

"Are you still here, my kind nurse?" said he, speaking


faintly, but with no appearance of wandering or
bewilderment. "Is it not very late? It seems as though I had
been sleeping for a long time."

"It is very late, or rather very early. It is just growing


daylight. You have slept soundly for several hours. How do
you feel?"

"Much better," replied Paul. "My dreams have been very


sweet. Did I dream it, or were you reading to me before I
went to sleep?

"You were not dreaming, dear brother," said Jack. "Have


you any recollection of what read?"
"Why do you call me brother?" asked Paul, with a wondering
look. "It is a dear name, but I never knew I had a brother."

"I call you so because I cannot but think that in one sense
we are brothers," said Jack. "But tell me, do you remember
what I read?"

"It could hardly be so," said Paul; "and yet—it seems to me


as if you had read to me from the Scripture. You are not a
priest, are you?" he asked, starting. "I fear I have been
saying more than I ought."

"Have no fears," returned Jack. "I am no priest, or priest's


tool, of that you may be sure, and you have betrayed
nothing. I did read from the Scripture to you last night,
because you desired it, and because I myself love the Book.
I could not betray you if I would, for I should myself stand
in the same peril."

"It is well," said Paul. "I am most thankful to have fallen


into such good hands. I do think I may trust you," he
added, looking wistfully into Jack's face. "But I have been
so betrayed by those in whom I have confided, that it has
sometimes seemed to me that I could never trust man
again."

"Have you no family friends near here?" asked Jack gently.


"I should think you a Somerset man by your speech."

"No—yes—indeed, I know not what to say on that matter,"


replied Paul, in an embarrassed tone. "I had once as kind
friends as ever lived, but I know not whether they would
own me now."

"Never mind," said Jack, who did not wish to agitate or


alarm his patient. "We will talk of that when you are
stronger, if you are disposed to give me your confidence. At
present, be sure you are among friends who will do all in
their power for you."

"I must think of it," said Paul, sinking back. "It is no mere
question of a shipwrecked sailor coming home in rags and
poverty, you know. I may tell you this: that my family are
gentlefolk of condition, and that they have good reason to
be angry with me since I have brought upon an ancient and
honorable house not only trouble but disgrace. There are
more interests than mine to be considered, you see, and
therefore I must weigh the matter well. I would gladly die,
if die I must, with my head on my father's breast; but not
even for that dear privilege would I bring a new pang to
rend his bosom."

"Think then, dear brother, think, but pray also," said Jack,
deeply moved. "You know the Apostle bids us, when we lack
wisdom, to ask it of God, nothing doubting, and it shall be
given us."

"I will indeed do so," replied Paul. "Nobody knows more


than I the value of prayer. But do you go home now, and go
to rest. I hear the good people of the house stirring."

Jack went home, but not to rest. He walked through the


quiet street in the crisp morning air, thinking what he had
better do. He did not know a great deal about sickness, but
he could see that Paul's state was critical. A very little might
turn the scale, so that there could be no recovery; and how
sad if he should die without being reconciled to his father!
From what he knew and guessed, Jack felt sure that there
would be no trouble between Paul and his father, on the
subject of religion. He walked three or four times up and
down the street, but at last he made up his mind.
"I will do it," said he. "I will do what lies in my power to
bring the father and son together. I will talk to my father,
and if he is willing I will borrow Master Felton's pony and set
out without delay."

CHAPTER XVI.

JACK'S ERRAND.

"Is my father up, Simon?" asked Jack, as he entered the


shop, which the journeyman was just putting to rights.

"I think not, Master Jack. I have not heard him stirring, and
he usually calls me to come truss his points for him."

"I will myself go up and help him to dress," said Jack, and
he went softly up-stairs to his father's room. Master Lucas
was just awake.

"So you have come home betimes," said he, rubbing his
eyes. "You have had a long watch and will be for taking a
good nap, I dare say, though you do not look very sleepy
either," he added, looking in his son's face. "You seem as if
you had heard some good news."

"And so I trust I have," said Jack. "I want to consult you,


dear father, about a matter of moment."
"Give me my gown, then," said his father. "It is time I was
up. Now let me hear the story."

Jack sat down on the side of the bed, and told his father of
the discovery he supposed himself to have made, with the
grounds of his belief. Master Lucas listened with attention.

"But supposing this young man to be the heir of Holford,"


said he, "do you think his father would receive him again?"

"I have good grounds for thinking so, which you shall hear,"
said Jack, and he repeated his reasons, which we already
know.

"Poor gentleman! My heart aches for him," said Master


Lucas. "But what is it you propose to do? You cannot take
Master Arthur to his home, weak as he is, even if he were
quite willing to go."

"No; and, therefore, I propose to bring his home to him,"


said Jack. "I propose to ride to Holford, see the knight, and
tell him all that I have told you. Then he can act as he
pleases."

"Have you said aught of your intention to Master Arthur—or


Paul, as he calls himself?"

"Not a word, dear father. I thought it best to be silent. Paul


—his name is Paul as well as Arthur—Paul is in doubt as to
his reception at home. He says he has brought shame and
disgrace on his honorable house, and he knows not whether
he ought to return—"

"So had the youth Father William preached about yesterday,


brought shame and disgrace on his family," interrupted the
baker. "Yet he returned, and his father received him gladly."
"And if the poor prodigal had been ill and starving,
repentant, and longing above all things for a sight of his
father's face, yet too weak and too fearful to go to him,"
said Jack, eagerly, "do you not think that he and his father
both would have been thankful to that man who had
brought them face to face, who had carried news to the
father that the son was languishing, perhaps dying, within
his reach? Make the case your own, dear father, and tell
me."

Master Lucas turned and looked at his son with tears in his
honest blue eyes. "Jack, you are a strange lad for your
years. I cannot understand what has made a man of you so
suddenly. Even do as you will, and manage the matter your
own way, my son. I cannot see what harm can come of it. If
the knight should refuse to see his son, the poor young
gentleman will at least be prevented from a bootless
journey."

"He will not refuse," said Jack. "Then, with your leave, dear
father, I will set out directly."

"As soon as you have rested a little and taken a good meal,
my son. Nay, I must insist upon that much, or we shall have
you ill again. Remember you are all the son, I had well-nigh
said all the child, I have in the world. Get you down and
send Simon to engage for your neighbor Fulford's pony. It is
an easy beast to ride, and faster than my mule. It is a
market-day, and the roads will be full of people, so you will
have nothing to fear from robbers, or I would send Simon
with you."

"I do not need him," said Jack. "Nobody would think of


robbing a lad like me; and besides I doubt Simon would be
no great safeguard. He has not the heart of a chicken.
Father," added Jack, earnestly, "I do heartily thank you for
trusting me so fully."

"When I see aught to distrust in you, it will be time to


begin," said Master Lucas. "My blessing upon thee, dear lad.
Thou hast never yet wilfully given thy father a heart-ache."

A pang shot through Jack's own breast, as he remembered


how soon he might be called upon to do and suffer that
which would wring his father's heart with anguish through
no fault of his own. "Oh, if it were only myself," he
reflected, as he sought his own chamber, "how easy it
would all be to endure." And, dearly as he loved his father,
Jack almost felt like praying that the good old man might be
taken away from the evil to come, before the storm burst
which Master Fleming had foretold.

Calmed and refreshed by his morning reading and prayers,


Jack came down to his breakfast dressed for his journey, his
sober, resolute face showing that his determination was
unshaken.

Cicely exclaimed against his setting out on such a ride after


he had been watching all night.

But Master Lucas made her a sign, and she said no more,
except to entreat her darling to eat and drink heartily, and
to put a comfortable morsel in his pocket, that he need not
be faint by the way.

She was dying with curiosity to learn the object of his


journey undertaken so suddenly, but she knew of old, that
unless Master Lucas chose to tell there was no use in
asking.

Anne was not so discreet. She came in when breakfast was


half over, from the priory church, where she had been
praying since four o'clock. Kneeling on cold stones for three
hours on a stretch without one's breakfast is not likely to
improve the temper, whatever other spiritual graces it may
impart. Anne felt weak, exhausted, and wretched, and all
ready, as her father said, to take the poker by the hot end.

"What is Simon doing, walking that horse up and down


before the door?" she asked, as she sat down. "Have some
of Jack's grand friends come to visit him so early?"

"I did not know that I had any grand friends," said Jack.

"I thought it might be Master Fleming," pursued Anne. "He


seems to use our house as his own at all times."

"If he does, he is no more free than welcome," said her


father. "I ever esteem his visits an honor as well as a
pleasure. But you are wrong this time. The pony is for no
less a person than our Jack, who is about to ride into the
country for some miles."

"Indeed!" said Anne. "And what takes him into the


country?"

"Business," replied her father briefly. "Business of


importance, which no one can well do but himself. Ask no
questions, sweetheart, for more I cannot tell you."

"I do not mean to ask any questions," said Anne, flushing.


"I know well that I am the last person to be trusted,
especially by Jack."

"Do you say so, Anne?" asked Jack, turning full upon her, as
his father left the room. "Methinks I have trusted you
already farther than you were willing to have me, farther
than I had reason to do, considering all things. But I do not
mean to reproach you, dear sister," he added, repenting,
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade

Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.

Let us accompany you on the journey of exploring knowledge and


personal growth!

textbookfull.com

You might also like