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

Functional Data Structures in R: Advanced Statistical Programming in R Thomas Mailund instant download

The document is a promotional and informational overview of the book 'Functional Data Structures in R: Advanced Statistical Programming' by Thomas Mailund. It discusses the challenges of implementing traditional mutable data structures in R, a primarily functional programming language, and introduces the concept of functional data structures as an alternative. The book aims to provide insights into algorithmic programming in R while highlighting the trade-offs between programming time and execution speed.

Uploaded by

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

Functional Data Structures in R: Advanced Statistical Programming in R Thomas Mailund instant download

The document is a promotional and informational overview of the book 'Functional Data Structures in R: Advanced Statistical Programming' by Thomas Mailund. It discusses the challenges of implementing traditional mutable data structures in R, a primarily functional programming language, and introduces the concept of functional data structures as an alternative. The book aims to provide insights into algorithmic programming in R while highlighting the trade-offs between programming time and execution speed.

Uploaded by

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

Functional Data Structures in R: Advanced

Statistical Programming in R Thomas Mailund


download

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

Download more ebook from https://textbookfull.com


We believe these products will be a great fit for you. Click
the link to download now, or visit textbookfull.com
to discover even more!

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/

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/

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/

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/
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/

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/

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/

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/

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/
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 [email protected], 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 [email protected], 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
Random documents with unrelated
content Scribd suggests to you:
and do you think you could get us some little thing now?'
'Here?' said he, with his face lighting up with pleasure: were
those three to have supper all by themselves?
'Oh yes,' said she, in her friendly way. 'I am not sure that my
mother would like me to stay at the inn for supper; but this is our
own place; and the table laid; and Maggie and I would rather be
here, I am sure. And you—are you not hungry too—after so long a
time—I am sure you want something besides raisins and shortbread.
But if it will be any trouble—
'Trouble or no trouble,' said he quickly, 'has nothing to do wi't.
Here, Maggie, lass, clear the end of the table; and we'll soon get
some supper for ye.'
And away he went to the inn, summoning the lasses there, and
driving and hurrying them until they had arranged upon a large tray
a very presentable supper—some cold beef, and ham, and cheese,
and bread, and ale; and when the fair-haired Nelly was ready to
start forth with this burden, he lit a candle and walked before her
through the darkness, lest she should miss her footing. And very
demure was Nelly when she placed this supper on the table; there
was not even a look for the smart young keeper; and when Meenie
said to her—
'I hear, Nelly, you had great goings-on on Monday night'—she
only answered—'Oh yes, miss, there was that'—and could not be
drawn into conversation, but left the moment she had everything
arranged.
But curiously enough, when the two girls had taken their seats
at this little cross table, Ronald remained standing—just behind
them, indeed, as if he were a waiter. And would Miss Douglas have
this? and would Miss Douglas have that? he suggested—mostly to
cloak his shamefacedness; for indeed that first wild assumption that
they were all to have supper together was banished now as an
impertinence. He would wait on them, and gladly; but—but his own
supper would come after.
'And what will you have yourself, Ronald?' Meenie asked.
'Oh,' said he, 'that will do by and by. I am not so hungry as
you.'
'Did you have so much of the shortbread?' said she, laughing.
He went and stirred up the peats—and the red glow sent a
genial warmth across towards them.
'Come, Ronald,' said the little Maggie, 'and have some supper.'
'There is no hurry,' he said evasively. 'I think I will go outside
and have a pipe now; and get something by and by.'
'I am sure,' said Meenie saucily, 'that it is no compliment to us
that you would rather go away and smoke. See, now, if we cannot
tempt you.'
And therewith, with her own pretty fingers, she made ready his
place at the table; and put the knife and fork properly beside the
plate; and helped him to a slice of beef and a slice of ham; and
poured some ale into his tumbler. Not only that, but she made a little
movement of arranging her dress which was so obviously an
invitation that he should there and then take a place by her, that it
was not in mortal man to resist; though, indeed, after sitting down,
he seemed to devote all his attention to looking after his
companions. And very soon any small embarrassment was entirely
gone; Meenie was in an unusually gay and merry mood—for she was
pleased that her party had been so obviously a success, and all her
responsibilities over. And this vivacity gave a new beauty to her face;
her eyes seemed more kind than ever; when she laughed, it was a
sweet low laugh, like the cooing of pigeons on a summer afternoon.
'And what are you thinking of, Maggie?' she said, suddenly
turning to the little girl, who had grown rather silent amid this
talking and joking.
'I was wishing this could go on for ever,' was the simple answer.
'What? A perpetual supper? Oh, you greedy girl! Why, you must
be looking forward to the Scandinavian heaven——'
'No, it's to be with Ronald and you, Meenie dear—just like now
—for you seem to be able to keep everybody happy.'
Miss Douglas did blush a little at this; but it was an honest
compliment, and it was soon forgotten. And then, when they had
finished supper, she said—
'Ronald, do you know that I have never played an
accompaniment to one of your songs? Would you not like to hear
how it sounds?
'But—but I'm not used to it—I should be putting you wrong——'
'No, no; I'm sure we will manage. Come along,' she said briskly.
'There is that one I heard you sing the other day—I heard you,
though you did not see me—"Gae bring to me a pint o' wine, and fill
it in a silver tassie; that I may drink, before I go, a service to my
bonnie lassie"—and very proud she was, I suppose. Well, now, we
will try that one.'
So they went to the other end of the barn, where the piano
was; and there was a good deal of singing there, and laughing and
joking—among this little party of three. And Meenie sang too—on
condition (woman-like) that Ronald would light his pipe. Little
Maggie scarcely knew which to admire the more—this beautiful and
graceful young lady, who was so complaisant and friendly and kind,
or her own brother, who was so handsome and manly and modest,
and yet could do everything in the world. Nor could there have been
any sinister doubt in that wish of hers that these three should always
be together as they were then; how was she to know that this was
the last evening on which Meenie Douglas and Ronald were to meet
on these all too friendly terms?

CHAPTER XI.
A REVELATION.

Early the next morning, when as yet the sunrise was still widening
up and over the loch, and the faint tinge of red had not quite left the
higher slopes of Clebrig, Ronald had already finished his breakfast,
and was in his own small room, smoking the customary pipe, and
idly—and with some curious kind of whimsical amusement in his
brain—turning over the loose sheets of scribbled verses. And that
was a very ethereal and imaginary Meenie he found there—a Meenie
of lonely hillside wanderings—a Meenie of daydreams and visions:
not the actual, light-hearted, shrewd-headed Meenie of the evening
before, who was so merry after the children had gone, and so
content with the little supper-party of three, and would have him
smoke his pipe without regard to her pretty silk dress. This Meenie
on paper was rather a wistful, visionary, distant creature; whereas
the Meenie of the previous evening was altogether good-humoured
and laughing, with the quaintest mother-ways in the management of
the children, and always a light of kindness shining in her clear
Highland eyes. He would have to write something to portray Meenie
(to himself) in this more friendly and actual character. He could do it
easily enough, he knew. There never was any lack of rhymes when
Meenie was the occasion. At other things he had to labour—
frequently, indeed, until, reflecting that this was not his business, he
would fling the scrawl into the fire, and drive it into the peats with
his heel, and go away with much content. But when Meenie was in
his head, everything came readily enough; all the world around
seemed full of beautiful things to compare with her; the birds were
singing of her; the mountains were there to guard her; the burn, as
it whispered through the rushes, or danced over the open bed of
pebbles, had but the one continual murmur of Meenie's name.
Verses? he could have written them by the score—and laughed at
them, and burned them, too.
Suddenly the little Maggie appeared.
'Ronald,' she said, 'the Doctor's come home.'
'What—at this time in the morning?' he said turning to her.
'Yes, I am sure; for I can see the dog-cart at the door of the
inn.'
'Well now,' said he, hastily snatching up his cap, 'that is a stroke
of luck—if he will come with us. I will go and meet him.'
But he need not have hurried so much; the dog-cart was still at
the door of the inn when he went out; and indeed remained there as
he made his way along the road. The Doctor, who was a most
sociable person, had stopped for a moment to hear the news; but
Mr. Murray happened to be there, and so the chat was a protracted
one. In the meantime Ronald's long swinging stride soon brought
him into their neighbourhood.
'Good morning, Doctor!' he cried.
'Good morning, Ronald,' said the other, turning round. He was a
big man, somewhat corpulent, with an honest, wholesome, ruddy
face, soft brown eyes, and an expressive mouth, that could temper
his very apparent good-nature with a little mild sarcasm.
'You've come back in the nick of time,' the keeper said—for well
he knew the Doctor's keen love of a gun. 'I'm thinking of driving
some of the far tops the day, to thin down the hares a bit; and I'm
sure ye'd be glad to lend us a hand.'
'Man, I was going home to my bed, to tell ye the truth,' said the
Doctor; 'it's very little sleep I've had the last ten days.'
'What is the use of that?' said Ronald, 'there's aye plenty o' time
for sleep in the winter.'
And then the heavy-framed occupant of the dog-cart glanced up
at the far-reaching heights of Clebrig, and there was a grim smile on
his mouth.
'It's all very well,' said he, 'for herring-stomached young fellows
like you to face a hill like that; but I've got weight to carry, man; and

'Come, come, Doctor; it's not the first time you've been on
Clebrig,' Ronald said—he could see that Meenie's father wanted to
be persuaded. 'Besides, we'll no try the highest tops up there—
there's been too much snow. And I'll tell ye how we'll make it easy
for ye; we'll row ye down the loch and begin at the other end and
work home—there, it's a fair offer.'
It was an offer, at all events, that the big doctor could not
withstand.
'Well, well,' said he, 'I'll just drive the dog-cart along and see
how they are at home; and then if the wife lets me out o' her
clutches, I'll come down to the loch side as fast as I can.'
Ronald turned to one of the stable-lads (all of whom were
transformed into beaters on this occasion).
'Jimmy, just run over to the house and fetch my gun; and bid
Maggie put twenty cartridges—number 4, she knows where they are
—into the bag; and then ye can take the gun and the cartridge-bag
down to the boat—and be giving her a bale-out till I come along. I'm
going to the farm now, to get two more lads if I can; tell the Doctor
I'll no be long after him, if he gets down to the loch first.'
Some quarter of an hour thereafter they set forth; and a rough
pull it was down the loch, for the wind was blowing hard, and the
waves were coming broadside on. Those who were at the oars had
decidedly the best of it, for it was bitterly cold; but even the others
did not seem to mind much—they were chiefly occupied in scanning
the sky-line of the hills (a habit that one naturally falls into in a deer
country), while Ronald and the Doctor, seated in the stern, were
mostly concerned about keeping their guns dry. In due course of
time they landed, made their way through a wood of young birch-
trees, followed the channel of a burn for a space, and by and by
began to reach the upper slopes, where the plans for the first drive
were carefully drawn out and explained.
Now it is unnecessary to enter into details of the day's
achievements, for they were neither exciting nor difficult nor daring.
It was clearly a case of shooting for the pot; although Ronald, in his
capacity of keeper, was anxious to have the hares thinned down,
knowing well enough that the over-multiplying of them was as
certain to bring in disease as the overstocking of a mountain farm
with sheep. But it may be said that the sport, such as it was, was
done in a workmanlike manner. In Ronald's case, each cartridge
meant a hare—and no praise to him, for it was his business. As for
the Doctor, he was not only an excellent shot, but he exercised a
wise and humane discretion as well. Nothing would induce him to
fire at long range on the off-chance of hitting; and this is all the
more laudable in the shooting of mountain hares, for these, when
wounded, will frequently dodge into a hole among the rocks, like a
rabbit, baffling dogs and men, and dying a miserable death.
Moreover, there was no need to take risky shots. The two guns were
posted behind a stone or small hillock—lying at full length on the
ground, only their brown-capped heads and the long barrels being
visible. Then the faint cries in the distance became somewhat louder
—with sticks rattled on rocks, and stones flung here and there;
presently, on the sky-line of the plateau, a small object appeared,
sitting upright and dark against the sky; then it came shambling
leisurely along—becoming bigger and bigger and whiter and whiter
every moment, until at length it showed itself almost like a cat, but
not running stealthily like a cat, rather hopping forward on its
ungainly high haunches; and then again it would stop and sit up, its
ears thrown back, its eyes not looking at anything in front of it, its
snow-white body, with here and there a touch of bluish-brown,
offering a tempting target for a pea-rifle. But by this time, of course,
numerous others had come hopping over the sky-line; and now as
the loud yells and shouts and striking of stones were close at hand,
there was more swift running instead of hobbling and pausing
among the white frightened creatures; and as they cared for nothing
in front (in fact a driven hare cannot see anything that is right ahead
of it, and will run against your boots if you happen to be standing in
the way), but sped noiselessly across the withered grass and hard
clumps of heather—bang! went the first barrel, and then another
and another, as quick as fingers could unload and reload, until here,
there, and everywhere—but always within a certain radius from the
respective posts—a white object lay on the hard and wintry ground.
The beaters came up to gather them together; the two guns had
risen from their cold quarters; there were found to be thirteen hares
all told—a quite sufficient number for this part—and not one had
crawled or hobbled away wounded.
But we will now descend for a time from these bleak altitudes
and return to the little hamlet—which seemed to lie there snugly
enough and sheltered in the hollow, though the wind was hard on
the dark and driven loch. Some hour or so after the shooters and
beaters had left, Meenie Douglas came along to Ronald's cottage,
and, of course, found Maggie the sole occupant, as she had
expected. She was very bright and cheerful and friendly, and spoke
warmly of Ronald's kindness in giving her father a day's shooting.
'My mother was a little angry,' she said, laughing, 'that he
should go away just the first thing after coming home; but you
know, Maggie, he is so fond of shooting; and it is not always he can
get a day, especially at this time of the year: and I am very glad he
has gone; for you know there are very few who have to work so
hard.'
'I wish they may come upon a stag,' said the little Maggie—with
reckless and irresponsible generosity.
'Do you know, Maggie,' said the elder young lady, with a shrewd
smile on her face, 'I am not sure that my mother likes the people
about here to be so kind; she is always expecting my father to get a
better post—but I know he is not likely to get one that will suit him
as well with the fishing and shooting. There is the Mudal—the
gentlemen at the lodge let him have that all the spring through; and
when the loch is not let, he can always have a day by writing to Mr.
Crawford; and here is Ronald, when the hinds have to be shot at
Christmas, and so on. And if the American gentleman takes the
shooting as well as the loch, surely he will ask my father to go with
him a day or two on the hill; it is a lonely thing shooting by one's
self. Well now, Maggie, did you put the curtains up again in Ronald's
room?'
'Yes, I did,' was the answer, 'and he did not tear them down this
time, for I told him you showed me how to hang them; but he has
tied them back so that they might just as well not be there at all.
Come and see, Meenie dear.'
She led the way into her brother's room; and there, sure
enough, the window-curtains (which were wholly unnecessary, by
the way, except from the feminine point of view, for there was
certainly not too much light coming in by the solitary window) had
been tightly looped and tied back, so that the view down the loch
should be unimpeded.
'No matter,' said Meenie; 'the window is not so bare-looking as it
used to be. And I suppose he will let them remain up now.'
'Oh yes, when he was told that you had something to do with
them,' was the simple answer.
Meenie went to the wooden mantelpiece, and put the few
things there straight, just as she would have done in her own room,
blowing the light white peat-dust off them, and arranging them in
neater order.
'I wonder, now,' she said, 'he does not get frames for these
photographs; they will be spoiled by finger marks and the dust.'
Maggie said shyly—
'That was what he said to me the other day—but not about
these—about the one you gave me of yourself. He asked to see it,
and I showed him how careful I was in wrapping it up; but he said
no—the first packman that came through I was to get a frame if he
had one, and glass too; or else that he would send it in to Inverness
to be framed. But you know, Meenie, it's not near so nice-looking—
or anything, anything like so nice-looking—as you are.'
'Nothing could be that, I am sure,' said Meenie lightly; and she
was casting her eyes about the room, to see what further
improvements she could suggest.
But Maggie had grown suddenly silent, and was standing at the
little writing-table, apparently transfixed with astonishment. It will be
remembered that when Ronald, in the morning, heard that the
Doctor was at the door of the inn, he had hurriedly hastened away
to intercept him; and that, subsequently, in order to save time, he
had sent back a lad for his gun and cartridges, while he went on to
the farm. Now it was this last arrangement that caused him to
overlook the fact that he had left his writing materials—the blotting-
pad and everything—lying exposed on the table; a piece of neglect
of which he had scarcely ever before been guilty. And as ill-luck
would have it, as Maggie was idly wandering round the room,
waiting for Meenie to make any further suggestions for the
smartening of it, what must she see lying before her, among these
papers, but a letter, boldly and conspicuously addressed?
'Well!' she exclaimed, as she took it up. 'Meenie, here is a letter
for you! why didna he send it along to you?'
'A letter for me?' Meenie said, with a little surprise. 'No! why
should Ronald write a letter to me?—I see him about every day.'
'But look!'
Meenie took the letter in her hand; and regarded the address;
and laughed.
'It is very formal,' said she. 'There is no mistake about it. "Miss
Wilhelmina Stuart Douglas"—when was I ever called that before?
And "Inver-Mudal, Sutherlandshire, N.B." He should have added
Europe, as if he was sending it from the moon. Well, it is clearly
meant for me, any way—oh, and open too——'
The next minute all the careless amusement fled from her face;
her cheeks grew very white, and a frightened, startled look sprang
to her eyes. She but caught the first few lines—

'O wilt thou be my dear love?


(Meenie and Meenie)
O wilt thou be my ain love?
(My sweet Meenie)?

and then it was with a kind of shiver that her glance ran over the
rest of it; and her heart was beating so that she could not speak;
and there was a mist before her eyes.
'Maggie,' she managed to say at length—and she hurriedly
folded up the paper again and placed it on the table with the others
—'I should not have read it—it was not meant for me—it was not
meant that I should read it—come away, come away, Maggie.'
She took the younger girl out of the room, and herself shut the
door, firmly, although her fingers were all trembling.
'Maggie,' she said, 'you must promise never to tell any one that
you gave me that letter—that I saw it——'
'But what is the matter, Meenie?' the smaller girl said in
bewilderment, for she could see by the strange half-frightened look
of Miss Douglas's face that something serious had happened.
'Well, it is nothing—it is nothing,' she forced herself to say. 'It
will be all right. I shouldn't have read the letter—it was not meant
for me to see—but if you say nothing about it, no harm will be done.
That's all; that's all. And now I am going to see if the children are
ready that are to go by the mail-car.'
'But I will go with you, Meenie.'
Then the girl seemed to recollect herself; and she glanced
round at the interior of the cottage, and at the little girl, with an
unusual kind of look.
'No, no, not this morning, Maggie,' she said. 'You have plenty to
do. Good-bye—good-bye!' and she stooped and kissed her, and
patted her on the shoulder, and left, seeming anxious to get away
and be by herself.
Maggie remained there in considerable astonishment. What had
happened? Why should she not go to help with the children? and
why good-bye—when Meenie would be coming along the road in
less than an hour, as soon as the mail-car had left? And all about the
reading of something contained in that folded sheet of paper.
However, the little girl wisely resolved that, whatever was in that
letter, she would not seek to know it, nor would she speak of it to
any one, since Meenie seemed so anxious on that point; and so she
set about her domestic duties again—looking forward to the end of
these and the resumption of her knitting of her brother's jersey.
Well, the winter's day went by, and they had done good work on
the hill. As the dusk of the afternoon began to creep over the
heavens, they set out for the lower slopes on their way home; and
very heavily weighted the lads were with the white creatures slung
over their backs on sticks. But the dusk was not the worst part of
this descent; the wind was now driving over heavy clouds from the
north; and again and again they would be completely enveloped,
and unable to see anywhere more than a yard from their feet. In
these circumstances Ronald took the lead; the Doctor coming next,
and following, indeed, more by sound than by sight; the lads
bringing up in the wake in solitary file, with their heavy loads
thumping on their backs. It was a ghostly kind of procession; though
now and again the close veil around them would be rent in twain,
and they would have a glimpse of something afar off—perhaps a
spur of Ben Loyal, or the dark waters of Loch Meidie studded with its
small islands. Long before they had reached Inver-Mudal black night
had fallen; but now they were on easier ground; and at last the firm
footing of the road echoed to their measured tramp, as the invisible
company marched on and down to the warmth and welcome lights
of the inn.
The Doctor, feeling himself something of a truant, went on
direct to his cottage; but the others entered the inn; and as Ronald
forthwith presented Mrs. Murray with half a dozen of the hares, the
landlord was right willing to call for ale for the beaters, who had had
a hard day's work. Nor was Ronald in a hurry to get home; for he
heard that Maggie was awaiting him in the kitchen; and so he and
Mr. Murray had a pipe and a chat together, as was their custom.
Then he sent for his sister.
'Well, Maggie, lass,' said he, as they set out through the dark,
'did you see all the bairns safely off this morning?'
'No, Ronald,' she said, 'Meenie did not seem to want me; so I
stayed at home.'
'And did you find Harry sufficient company for ye? But I suppose
Miss Douglas came and stayed with ye for a while.'
'No, Ronald,' said the little girl, in a tone of some surprise; 'she
has not been near the house the whole day, since the few minutes in
the morning.'
'Oh,' said he, lightly, 'she may have been busy, now her father is
come home. And ye maun try and get on wi' your lessons as well as
ye can, lass, without bothering Miss Douglas too much; she canna
always spend so much time with ye.'
The little girl was silent. She was thinking of that strange
occurrence in the morning of which she was not to speak; and in a
vague kind of way she could not but associate that with Meenie's
absence all that day, and also with the unusual tone of her 'good-
bye.' But yet, if there were any trouble, it would speedily pass away.
Ronald would put everything right. Nobody could withstand him—
that was the first and last article of her creed. And so, when they got
home, she proceeded cheerfully enough to stir up the peats, and to
cook their joint supper in a manner really skilful for one of her years;
and she laid the cloth; and put the candles on the table; and had the
tea and everything ready. Then they sate down; and Ronald was in
very good spirits, and talked to her, and tried to amuse her. But the
little Maggie rather wistfully looked back to the brilliant evening
before, when Meenie was with them; and perhaps wondered
whether there would ever again be a supper-party as joyful and
friendly and happy as they three had been when they were all by
themselves in the big gaily-lit barn.

CHAPTER XII.
'WHEN SHADOWS FALL.'

The deershed adjoining the kennels was a gloomy place, with its
bare walls, its lack of light, and its ominous-looking crossbeams,
ropes, and pulley for hanging up the slain deer; and the morning
was dark and lowering, with a bitter wind howling along the glen,
and sometimes bringing with it a sharp smurr of sleet from the
northern hills. But these things did not seem to affect Ronald's spirits
much as he stood there, in his shirt-sleeves, and bare-headed,
sorting out the hares that were lying on the floor, and determining to
whom and to whom such and such a brace or couple of brace should
be sent. Four of the plumpest he had already selected for Mrs.
Douglas (in the vague hope that the useful present might make her
a little more placable), and he was going on with his choosing and
setting aside—sometimes lighting a pipe—sometimes singing
carelessly—

'O we aft hae met at e'en, bonnie Peggie, O,


On the banks o' Cart sae green, bonnie Peggie, O,
Where the waters smoothly rin,
Far aneath the roarin' linn,
Far frae busy strife and din, bonnie Peggie, O'—

when the little Maggie came stealing in.


'Ronald,' she said, with an air of reproach, 'why are ye going
about on such a morning without your jacket, and bare-headed,
too?'
'Toots, toots, lassie, it's a fine morning,' said he indifferently.
'It was Meenie said I was not to let you do such foolish things,'
the little lass ventured to say diffidently.
Of course this put a new aspect on the case, but he would not
admit as much directly.
'Oh, well,' said he, 'if you bring me out my coat and bonnet I
will put them on, for I'm going down to the Doctor's with two or
three of the hares.'
And then she hesitated.
'Ronald,' said she, 'I will take them to Mrs. Douglas, if you like.'
'You?' said he.
'For I would give them to her with a nice message from you;
and—and—if you take them, you will say nothing at all; and where is
the compliment?'
He laughed.
'Ye're a wise little lass; but four big hares are heavy to carry—
with the wind against ye; so run away and get me my coat and my
Glengarry; and I will take them along myself, compliment or no
compliment.'
However, as it turned out, Mrs. Douglas was not the first of the
family he was fated to meet that morning. He had scarcely left the
deershed when he perceived Meenie coming along the road; and
this was an auspicious and kindly event; for somehow the day
seemed to go by more smoothly and evenly and contentedly when
he had chanced to meet Meenie in the morning, and have a few
minutes' chat with her about affairs in general, and an assurance
that all was going well with her. So he went forward to meet her
with a light heart; and he thought she would be pleased that he was
taking the hares to her mother; and perhaps, too, he considered
that they might be a little more frank in their friendship after the
exceeding good fellowship of the night of the children's party.
He went forward unsuspectingly.
'Good morning, Miss Douglas!' said he, slackening in his pace,
for naturally they always stopped for a few seconds or minutes when
they met thus.
But to his astonishment Miss Douglas did not seem inclined to
stay. Her eyes were bent on the ground as she came along; she but
timidly half lifted them as she reached him; and 'Good morning,
Ronald!' she said, and would have passed on. And then it seemed as
if, in her great embarrassment, she did not know what to do. She
stopped; her face was suffused with red; and she said hurriedly—
and yet with an effort to appear unconcerned—
'I suppose Maggie is at home?'
'Oh yes,' said he, and her manner was so changed that he also
scarce knew what to say or to think.
And again she was going on, and again she lingered—with a
sudden fear that she might be thought ungracious or unkind.
'The children all got away safely yesterday morning,' said she—
but her eyes never met his; and there was still tell-tale colour in her
cheeks.
'So I heard,' he answered.
'I am sure they must have enjoyed the evening,' she said, as if
forcing herself to speak.
And then it suddenly occurred to him—for this encounter had
been all too brief and bewildering for any proper understanding of it
—that perhaps her mother had been reproving her for being too
friendly with the people about the inn and with himself, and that he
was only causing her embarrassment by detaining her, and so he
said—
'Oh yes, I'm sure o' that. Well, good morning, Miss Douglas; I'm
going along to give your mother these two or three hares.'
'Good morning,' said she—still without looking at him—and then
she went.
And he, too, went on his way; but only for a brief space;
presently he sate down on the low stone dyke by the roadside, and
dropped the hares on the ground at his feet. What could it all mean?
She seemed anxious to limit their acquaintanceship to the merest
formalities; and yet to be in a manner sorry for having to do so. Had
he unwittingly given her some cause of offence? He began to recall
the minutest occurrences of the night of the children's party—
wondering if something had then happened to account for so
marked a change? But he could think of nothing. The supper-party
of three was of her own suggestion; she could not be angry on that
account. Perhaps he ought to have asked this person or that person
over from the inn to join them, for the sake of propriety? Well, he
did not know much about such matters; it seemed to him that they
were very happy as they were; and that it was nobody else's
business. But would she quarrel with him on that account? Or on
account of his smoking in her presence? Again and again he wished
that his pipe had been buried at the bottom of the loch; and indeed
his smoking of it that evening had given him no enjoyment
whatever, except in so far as it seemed to please her; but surely, in
any case, that was a trifle? Meenie would not suddenly become cold
and distant (in however reluctant a way) for a small matter like that?
Nor could she be angry with him for taking her father away for a day
on the hill; she was always glad when the Doctor got a day's
shooting from anybody. No; the only possible conclusion he could
come to was that Mrs. Douglas had more strongly than ever
disapproved of Meenie's forming friendships among people not of
her own station in life; and that some definite instructions had been
given, which the girl was anxious to obey. And if that were so, ought
he to make it any the more difficult for her? He would be as
reserved and distant as she pleased. He knew that she was a very
kindly and sensitive creature; and might dread giving pain; and
herself suffer a good deal more than those from whom she was in a
measure called upon to separate herself. That was a reason why it
should be made easy for her; and he would ask Maggie to get on
with her lessons by herself, as much as she could; and when he met
Miss Douglas on the road, his greeting of her would be of the
briefest—and yet with as much kindness as she chose to accept in a
word or a look. And if he might not present her with the polecat's
skin that was now just about dressed?—well, perhaps the American
gentleman's daughter would take it, and have it made into
something, when she came up in March.
The pretty, little, doll-like woman, with the cold eyes and the
haughty stare, was at the front-door of the cottage, scattering food
to the fowls.
'I have brought ye two or three hares, Mrs. Douglas, if they're
of any use to ye,' Ronald said modestly.
'Thank you,' said she, with lofty courtesy, 'thank you; I am
much obliged. Will you step in and sit down for a few minutes?—I
am sure a little spirits will do you no harm on such a cold morning.'
In ordinary circumstances he would have declined that
invitation; for he had no great love of this domineering little woman,
and much preferred the society of her big, good-natured husband;
but he was curious about Meenie, and even inclined to be resentful,
if it appeared that she had been dealt with too harshly. So he
followed Mrs. Douglas into the dignified little parlour—which was
more like a museum of cheap curiosities than a room meant for
actual human use; and forthwith she set on the crimson-dyed table-
cover a glass, a tumbler, a jug of water, and a violet-coloured
bulbous glass bottle with an electro-plated stopper. Ronald was
bidden to help himself; and also, out of her munificence, she put
before him a little basket of sweet biscuits.
'I hear the Doctor is away again,' Ronald said—and a hundred
times would he rather not have touched the violet bottle at all,
knowing that her clear, cold, blue eyes were calmly regarding his
every movement.
'Yes,' she said, 'to Tongue. There is a consultation there. I am
sure he has had very little peace and quiet lately.'
'I am glad he had a holiday yesterday,' Ronald said, with an
endeavour to be agreeable.
But she answered severely—
'It might have been better if he had spent the first day of his
getting back with his own family. But that has always been his way;
everything sacrificed to the whim of the moment—to his own likings
and dislikings.'
'He enjoys a day's sport as much as any man I ever saw,' said
he—not knowing very well what to talk about.
'Yes, I daresay,' she answered shortly.
Then she pushed the biscuits nearer him; and returned to her
attitude of observation, with her small, neat, white hands crossed on
her lap, the rings on the fingers being perhaps just a little displayed.
'Miss Douglas is looking very well at present.' he said, at a
venture.
'Williamina is well enough—she generally is,' she said coldly.
'There is never much the matter with her health. She might attend
to her studies a little more and do herself no harm. But she takes
after her father.'
There was a little sigh of resignation.
'Some of us,' said he good-naturedly, 'were expecting her to
come over on Monday night to see the dancing.'
But here he had struck solid rock. In a second—from her
attitude and demeanour—he had guessed why it was that Meenie
had not come over to the landlord's party: a matter about which he
had not found courage to question Meenie herself.
'Williamina,' observed the little dame, with a magnificent dignity,
'has other things to think of—or ought to have, at her time of life,
and in her position. I have had occasion frequently of late to remind
her of what is demanded of her; she must conduct herself not as if
she were for ever to be hidden away in a Highland village. It will be
necessary for her to take her proper place in society, that she is
entitled to from her birth and her relatives; and of course she must
be prepared—of course she must be prepared. There are plenty who
will be willing to receive her; it will be her own fault if she
disappoints them—and us, too, her own parents. Williamina will
never have to lead the life that I have had to lead, I hope; she
belongs by birth to another sphere; and I hope she will make the
most of her chances.'
'Miss Douglas would be made welcome anywhere, I am sure,'
he ventured to say; but she regarded him with a superior look—as if
it were not for him to pronounce an opinion on such a point.
'Soon,' she continued—and she was evidently bent on
impressing him, 'she will be going to Glasgow to finish in music and
German, and to get on with her Italian: you will see she has no time
to lose in idle amusement. We would send her to Edinburgh or to
London, but her sister being in Glasgow is a great inducement; and
she will be well looked after. But, indeed, Williamina is not the kind
of girl to go and marry a penniless student; she has too much
common sense; and, besides, she has seen how it turns out. Once in
a family is enough. No; we count on her making a good marriage, as
the first step towards her taking the position to which she is entitled;
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