From f9a73d48db9ff1a078809843abcede2d73ae774d Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sun, 26 May 2019 13:09:44 -0700 Subject: [PATCH] lang: "range" function This is similar to the function of the same name in Python, generating a sequence of numbers as a list that can then be used in other sequence-oriented operations. The primary use-case for it is to turn a count expressed as a number into a list of that length, which can then be iterated over or passed to a collection function to produce that number of something else, as shown in the example at the end of its documentation page. --- lang/functions.go | 1 + lang/functions_test.go | 28 ++++ .../configuration/functions/range.html.md | 146 ++++++++++++++++++ website/layouts/functions.erb | 4 + 4 files changed, 179 insertions(+) create mode 100644 website/docs/configuration/functions/range.html.md diff --git a/lang/functions.go b/lang/functions.go index 2c7b5482b..5fe0558d8 100644 --- a/lang/functions.go +++ b/lang/functions.go @@ -85,6 +85,7 @@ func (s *Scope) Functions() map[string]function.Function { "min": stdlib.MinFunc, "pathexpand": funcs.PathExpandFunc, "pow": funcs.PowFunc, + "range": stdlib.RangeFunc, "replace": funcs.ReplaceFunc, "reverse": funcs.ReverseFunc, "rsadecrypt": funcs.RsaDecryptFunc, diff --git a/lang/functions_test.go b/lang/functions_test.go index 8717ec7cd..8ae3de12d 100644 --- a/lang/functions_test.go +++ b/lang/functions_test.go @@ -513,6 +513,34 @@ func TestFunctions(t *testing.T) { }, }, + "range": { + { + `range(3)`, + cty.ListVal([]cty.Value{ + cty.NumberIntVal(0), + cty.NumberIntVal(1), + cty.NumberIntVal(2), + }), + }, + { + `range(1, 4)`, + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + cty.NumberIntVal(3), + }), + }, + { + `range(1, 8, 2)`, + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(3), + cty.NumberIntVal(5), + cty.NumberIntVal(7), + }), + }, + }, + "replace": { { `replace("hello", "hel", "bel")`, diff --git a/website/docs/configuration/functions/range.html.md b/website/docs/configuration/functions/range.html.md new file mode 100644 index 000000000..daaab05bf --- /dev/null +++ b/website/docs/configuration/functions/range.html.md @@ -0,0 +1,146 @@ +--- +layout: "functions" +page_title: "range - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-range" +description: |- + The range function generates sequences of numbers. +--- + +# `range` Function + +-> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and +earlier, see +[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). + +`range` generates a list of numbers using a start value, a limit value, +and a step value. + +```hcl +range(max) +range(start, limit) +range(start, limit, step) +``` + +The `start` and `step` arguments can be omitted, in which case `start` defaults +to zero and `step` defaults to either one or negative one depending on whether +`limit` is greater than or less than `start`. + +The resulting list is created by starting with the given `start` value and +repeatedly adding `step` to it until the result is equal to or beyond `limit`. + +The interpretation of `limit` depends on the direction of `step`: for a positive +step, the sequence is complete when the next number is greater than or equal +to `limit`. For a negative step, it's complete when less than or equal. + +The sequence-building algorithm follows the following pseudocode: + +``` +let num = start +while num <= limit: (or, for negative step, num >= limit) + append num to the sequence + num = num + step +return the sequence +``` + +Because the sequence is created as a physical list in memory, Terraform imposes +an artificial limit of 1024 numbers in the resulting sequence in order to avoid +unbounded memory usage if, for example, a very large value were accidentally +passed as the limit or a very small value as the step. If the algorithm above +would append the 1025th number to the sequence, the function immediately exits +with an error. + +We recommend iterating over existing collections where possible, rather than +creating ranges. However, creating small numerical sequences can sometimes +be useful when combined with other collections in collection-manipulation +functions or `for` expressions. + +## Examples + +``` +> range(3) +[ + 0, + 1, + 2, +] + +> range(1, 4) +[ + 1, + 2, + 3, +] + +> range(1, 8, 2) +[ + 1, + 3, + 5, + 7, +] + +> range(1, 4, 0.5) +[ + 1, + 1.5, + 2, + 2.5, + 3, + 3.5, +] + +> range(4, 1) +[ + 4, + 3, + 2, +] + +> range(10, 5, -2) +[ + 10, + 8, + 6, +] +``` + +The `range` function is primarily useful when working with other collections +to produce a certain number of instances of something. For example: + +```hcl +variable "name_counts" { + type = map(number) + default = { + "foo" = 2 + "bar" = 4 + } +} + +locals { + expanded_names = { + for name, count in var.name_counts : name => [ + for i in range(count) : format("%s%02d", name, i) + ] + } +} + +output "expanded_names" { + value = local.expanded_names +} + +# Produces the following expanded_names value when run with the default +# "name_counts": +# +# { +# "bar" = [ +# "bar00", +# "bar01", +# "bar02", +# "bar03", +# ] +# "foo" = [ +# "foo00", +# "foo01", +# ] +# } +``` diff --git a/website/layouts/functions.erb b/website/layouts/functions.erb index 87f4fffc5..2e333d046 100644 --- a/website/layouts/functions.erb +++ b/website/layouts/functions.erb @@ -182,6 +182,10 @@ merge +
  • + range +
  • +
  • reverse