Create index of definitions / theorems at end of b

2019-04-09 23:06发布

问题:

For reader convenience, I'd like to include, at the end of my bookdown book, written in markdown, a simple list or index of definitions from the body of the book. i.e. ones created using custom blocks, like this:

```{definition, bar, echo=T}
A bar is defined here as a foo-like thing.
```

(My need is for definitions, but others might like lists of theorems, etc. Don't know if lists of figures and tables could be covered in the same way?)

Thanks to @yihui I know that knitr::all_labels(engine == 'definition') is my friend.

So I can do this anywhere in the end of the book, usually at the end:

```{r comment="",results="asis",echo=FALSE}
knitr::all_labels(engine == 'definition') %>% unlist %>% paste0("\n\n","\\@ref(def:",.,"): ",.,"\n\n",collapse="\n\n") %>% cat

```

Which prints this:

1: bar

2: foobar

With clickable numbers. Which is OK. But wouldn't it be nice if, after each label, the actual definition could be printed too? (The content of the blocks is not available in knitr::all_labels(engine == 'definition'))

回答1:

Here is an example using the output format bookdown::html_document2, and it should also work for any other book output formats:

---
title: "Test Definitions"
output: bookdown::html_document2
---

```{r setup, include=FALSE}
def_list = list()
knitr::knit_hooks$set(engine = function(before, options) {
  if (before && options$engine == 'definition') {
    # collect definition terms from options$name
    def_list[[options$label]] <<- options$name
  }
  NULL
})
```

```{definition, d1, name='Foo'}
Foo is defined as ...
```

```{definition, d2, name='Bar'}
Bar is defined as ...
```

All definitions in this document:

```{r echo=FALSE, results='asis'}
def_list = unlist(def_list)
cat(sprintf('- \\@ref(def:%s) %s', names(def_list), def_list), sep = '\n')
```

Output:

The basic idea is to use a chunk hook to collect definition labels and names, and print them at the end. You do not have to use the chunk option name. It can be an arbitrary option, e.g., term. The option name is special because the name of the definition will be printed in the output. If you don't like that, you can use, for example, term:

```{definition, d2, term='Bar'}
Bar is defined as ...
```


回答2:

Perfect, so adding on from Yihui's suggestion, this prints out the definitions as well, and no need to bother with names, just labels will do:

```{definition, 'Bar',echo=T,cache=F}
Bar is defined as something
```

```{definition, 'Bar2',echo=T,cache=F}
Bar2 is defined as something else.
```

Here are all the definitions in this book.

```{r comment="",results="asis",echo=FALSE,cache=F}


for(x in knitr::all_labels(engine == 'definition')){
   paste0("\n\n","\\@ref(def:",x,"): ",x,"\n\n>",knitr:::knit_code$get(x),collapse="\n\n") %>% cat
} 

```

... produces this:

Here are all the definitions in this book.

1: Bar

Bar is defined as something

2: Bar2

Bar2 is defined as something else.