6.18 — Introduction to standard library algorithms

New programmers typically spend a lot of time writing custom loops to perform relatively simple tasks, such as sorting or counting or searching arrays. These loops can be problematic, both in terms of how easy it is to make an error, and in terms of overall maintainability, as loops can be hard to understand.

Because searching, counting, and sorting are such common operations to do, the C++ standard library comes with a bunch of functions to do these things in just a few lines of code. Additionally, these standard library functions come pre-tested, are efficient, work on a variety of different container types, and many support parallelization (the ability to devote multiple CPU threads to the same task in order to complete it faster).

The functionality provided in the algorithms library generally fall into one of three categories:

  • Inspectors -- Used to view (but not modify) data in a container. Examples include searching and counting.
  • Mutators -- Used to modify data in a container. Examples include sorting and shuffling.
  • Facilitators -- Used to generate a result based on values of the data members. Examples include objects that multiply values, or objects that determine what order pairs of elements should be sorted in.

These algorithms live in the algorithms library. In this lesson, we’ll explore some of the more common algorithms -- but there are many more, and we encourage you to read through the linked reference to see everything that’s available!

Note: All of these make use of iterators, so if you’re not familiar with basic iterators, please review lesson 6.17 -- Introduction to iterators.

Using std::find to find an element by value

std::find searches for the first occurrence of a value in a container. std::find takes 3 parameters: an iterator to the starting element in the sequence, an iterator to the ending element in the sequence, and a value to search for. It returns an iterator pointing to the element (if it is found) or the end of the container (if the element is not found).

For example:

Sample run when the element is found

Enter a value to search for and replace with: 5 234
13 90 99 234 40 80

Sample run when the element isn’t found

Enter a value to search for and replace with: 0 234
Could not find 0
13 90 99 5 40 80

Using std::find_if to find an element that matches some condition

Sometimes we want to see if there is a value in a container that matches some condition (e.g. a string that contains a specific substring) rather than an exact value. In such cases, std::find_if is perfect. The std::find_if function works similarly to std::find, but instead of passing in a value to search for, we pass in a callable object, such as a function pointer (or a lambda, which we’ll cover later) that checks to see if a match is found. std::find_if will call this function for every element until a matching element is found (or no more elements remain in the container to check).

Here’s an example where we use std::find_if to check if any elements contain the substring “nut”:


Found walnut

If you were to write the above example by hand, you’d need at least three loops (one to loop through the array, and two to match the substring). The standard library functions allow us to do the same thing in just a few lines of code!

Using std::count and std::count_if to count how many occurrences there are

std::count and std::count_if search for all occurrences of an element or an element fulfilling a condition.

In the following example, we’ll count how many elements contain the substring “nut”:


Counted 2 nut(s)

Using std::sort to custom sort

We previously used std::sort to sort an array in ascending order, but std::sort can do more than that. There’s a version of std::sort that takes a function as its third parameter that allows us to sort however we like. The function takes two parameters to compare, and returns true if the first argument should be ordered before the second. By default, std::sort sorts the elements in ascending order.

Let’s use std::sort to sort an array in reverse order using a custom comparison function named greater:


99 90 80 40 13 5

Once again, instead of writing our own custom loop functions, we can sort our array however we like in just a few lines of code!

Our greater function needs 2 arguments, but we’re not passing it any, so where do they come from? When we use a function without parentheses (), it’s only a function pointer, not a call. You might remember this from when we tried to print a function without parentheses and std::cout printed “1”. std::sort uses this pointer and calls the actual greater function with any 2 elements of the array. We don’t know which elements greater will be called with, because it’s not defined which sorting algorithm std::sort is using under the hood. We talk more about function pointers in chapter 7.


Because sorting in descending order is so common, C++ provides a custom type (named std::greater) for that too (which is part of the functional header). In the above example, we can replace:


Note that the std::greater{} needs the curly braces because it is not a callable function. It’s a type, and in order to use it, we need to instantiate an object of that type. The curly braces instantiate an anonymous object of that type (which then gets passed as an argument to std::sort).

For advanced readers

To further explain how std::sort uses the comparison function, we’ll have to take a step back to a modified version of the selection sort example from lesson 6.4 -- Sorting an array using selection sort.

So far, this is nothing new and sort always sorts elements from low to high. To add a comparison function, we have to use a new type, std::function, to store a function that takes 2 int parameters and returns a bool. Treat this type as magic for now, we will explain it in chapter 7.

We can now pass a comparison function like greater to sort, but how does sort use it? All we need to do is replace the line


Now the caller of sort can chose how to compare two elements.

Using std::for_each to do something to all elements of a container

std::for_each takes a list as input and applies a custom function to every element. This is useful when we want to perform the same operation to every element in a list.

Here’s an example where we use std::for_each to double all the numbers in an array:


2 4 6 8

This often seems like the most unnecessary algorithm to new developers, because equivalent code with a range-based for-loop is shorter and easier. But there are benefits to std::for_each. Let’s compare std::for_each to a range-based for-loop.

With std::for_each, our intentions are clear. Call doubleNumber with each element of arr. In the range-based for-loop, we have to add a new variable, i. This leads to several mistakes that a programmer could do when they’re tired or not paying attention. For one, there could be an implicit conversion if we don’t use auto. We could forget the ampersand, and doubleNumber wouldn’t affect the array. We could accidentally pass a variable other than i to doubleNumber. These mistakes cannot happen with std::for_each.

Additionally, std::for_each can skip elements at the beginning or end of a container, for example to skip the first element of arr, std::next can be used to advance begin to the next element.

This isn’t possible with a range-based for-loop.

Like many algorithms, std::for_each can be parallelized to achieve faster processing, making it better suited for large projects and big data than a range-based for-loop.

Order of execution

Note that most of the algorithms in the algorithms library do not guarantee a particular order of execution. For such algorithms, take care to ensure any functions you pass in do not assume a particular ordering, as the order of invocation may not be the same on every compiler.

The following algorithms do guarantee sequential execution: std::for_each, std::copy, std::copy_backward, std::move, and std::move_backward.

Best practice

Unless otherwise specified, do not assume that standard library algorithms will execute in a particular sequence. std::for_each, std::copy, std::copy_backward, std::move, and std::move_backward have sequential guarantees.

Ranges in C++20

Having to explicitly pass arr.begin() and arr.end() to every algorithm is a bit annoying. But fear not -- C++20 adds ranges, which allow us to simply pass arr. This will make our code even shorter and more readable.


The algorithms library has a ton of useful functionality that can make your code simpler and more robust. We only cover a small subset in this lesson, but because most of these functions work very similarly, once you know how a few work, you can make use of most of them.

Best practice

Favor using functions from the algorithms library over writing your own functionality to do the same thing

6.x -- Chapter P.6 comprehensive quiz
6.17 -- Introduction to iterators

81 comments to 6.18 — Introduction to standard library algorithms

  • yeokaiwei

    1. Feedback. I think it is better to teach std::find and std::sort earlier.

    The Best Practice literally states
    "Favor using functions from the algorithms library over writing your own functionality to do the same thing"

    It is best to teach this in Chapter 1, 2 or 3.

    2. On line 34,
    ::sort(std::begin(array), std::end(array), std::greater{});

    You have an additional ::.

    Is this a typo?

    3. Feedback. Giving the Standard Library FIRST is extremely important as it gives students a "panic paperbag". If they don't know how to solve it on their own, refer to the standard library. Each time they want to strangle something, refer to the standard library. Each time there is a crash, refer to the standard library.

    It's like a panic button.

    • nascardriver

      1. and 3. The standard library is important, but would have to be treated as a magic box until some time later. People should learn how functionality of the standard library works. If we show the standard library first, and then show how it's implemented, people won't pay attention because they can simply use the standard library. By teaching the implementation first we're not showing seemingly unnecessary implementations.

      2. There's a comment above that line explaining why it has to be `::sort()`

      • yeokaiwei

        1. and 3. Ok, how about a cheatsheet?

        2. Oh, ::sort is  a global namespace. If it's empty, it's a global namespace.

        Thank you for taking the time to answer my silly comments.

        • nascardriver

          A cheat sheet for best practices/rules as you suggested before?
          I think this would be useful, but I don't think it's worth the effort. You can use the cpp core guidelines, which is a huge collection of best practices. You can also use clang-tidy, which will give you real-time warnings (Including some from the core guidelines) about potentially bad code.

          • yeokaiwei

            Yes, please.

            If you could compile all best practices + warnings into a single sheet, we could print it out and leave it next to us.

            You can update it as time passes.

            E.g. int days{};, NOT int d;
            E.g. ++i, NOT i++
            E.g. std::vector, not linked lists
            E.g. Favor using functions from the algorithms library over writing your own functionality to do the same thing

  • srt1104

    I tried sorting the array using

    rather than

    in the std::sort function and it works. Can you explain how () and {} differ in that regard?

    • nascardriver

      One is direct initialization, the other is list initialization. The differences are the same as for all other types.
      List initialization is universal.
      List initialization doesn't suffer from the most vexing parse.
      List initialization can initialize to a default value.
      List initialization can initialize lists.

      In this specific case, there is no difference.

  • Martin

    In the explanation of the comparison function passed to sort() the global namespace selector is used:

    I don't understand why leaving out the global namespace selector here would lead to a collision. Without a using directive or statement, the compiler should be able to tell sort and std::sort apart (and indeed GCC is happy without the double colon). Could you please elucidate the potential collision?

    • nascardriver

      See my comment here

      Specifically what you're asking about is argument dependent lookup. If an argument is a member of a namespace, the name of the function is looked up in that namespace.

  • Martin

    In the section on custom sort, there is a code example containing the comment

    To me it looks like the @ sign is used to mark a variable. However, this has not been introduced or used earlier in the tutorial, so I would suggest to explain it or to leave it out.

  • KoffMan

    Any way to use find_if with user input? This works but it makes me type "nut" every time find_if calls the function until it finds walnut:

  • Szilard

    I copied the find_if implementation to Visual Studio Version 16.7.0 and set the Language Standard to C++17 and I'm getting "namespace std has no member string_view error.
    Any ideas why?

  • samuel

    Hi, is it possible to add user input in the std::find_if example? How would you replace "nut" with a user inputed string? Thanks.

    • nascardriver

      Yes that's possible. That's exactly what we'll do in lesson 7.16 using a lambda.

      You can also do this by creating a new function using `std::bind`

      `std::bind` in this case creates a new function that has 1 parameter (_1). This new function calls `contains` with `_1` as the first argument and "sausage" as the second argument. Kind of like this

  • PM_Me_Username_Ideas

    Hi. My end goal is to be able to solve Data Structure and Algorithm problems. I'm worried that if I get accustomed to using these standard algorithms, it would affect my ability to create my own algorithms from scratch. Is this the case? Also, could you recommend an online(non-video) resource to help me with DS&A? Thanks

    • Yes, these standard algorithms are meant for the following reasons:

      1. They're generic (work for different data types), robust (type-safe and scalable), portable (work on all the platforms viz. Unix, Windows etc.) and tried-n-tested (have a global acceptance)
      2. They're easy to use which is very much required when you write production-level code so that you focus on "what" to do rather than "how" to do.
      3. It's a common language that all the developers speak, and hence a different pair of eye won't face any issue.
      4. There are other reasons too which one likes as and when she/he adopts these Algos.

      However, having said that, yes you should be able to write (or at least understand) the implementations of the underlying Data structures and algorithms so that you can opt for the best tool for your need. E.g. prefer using std::string_view instead of const std::string& (well not always but mostly yes), or when to choose std::unordered_map instead of std::ordered_map (hint: think Hashtable vs RB-tree). Moreover, it's always fun :)

      Regarding your query of DS and Algo, your best bet should be a good textbook like 'Algorithms' by Robert Sedgwick or 'Algorithms Design Manual' by Steven Skiena. If you're interested in online links, you may wish to refer the following links: (they have got a link for Algo too) (there are other links too on this site)

      Hope it helps. I wish you good luck

  • Alek

    hello,in the part where you are introduing find_if you say :If you were to write the above example by hand, you’d need at least two loops (one to loop through the array, and one to match the substring). The standard library functions allow us to do the same thing in just a few lines of code!. I don't know why would we need two loops for it.... this is the code I've written for it and it's only using one loop:

    if I'm doing it wrong please clarify me and guide me how to write this function manually thanks!

    • nascardriver

      You're calling `find`, which uses a loop. You could do it in a single loop, but the general approach is to use 2 loops.

      • Alek

        you mean find is using a loop to match the given words letter by letter internally right ?
        if the above code of mine works fine what's the reason to use two loops ? can you use two loops and recreate above functionality? thanks

        • Alek

          I just don't get the Idea of using two loops.I mean I dunno how to define the second should just look like the last one ? could you please implement it ?

          • nascardriver

            Three loops actually, I updated the lesson. One loops to loop through the words, two loops to check if a word contains the substring.
            Say we want to search for "ate" in "heater"
            Start with the first letter of each word
            a == h? No, skip h and try the next letter
            a == e? No, skip e and try the next letter
            a == a? Yes, continue with the next letter in each word
            t == t? Yes, continue
            e == e? Yes and we're at the end of "ate", so the substring was found.

            We're basically shifting the substring through the text and check if the text at the current index matches the substring

  • code won't work

    1: when I tried to run the final version of sorting an array for the part of "advanced users", I found that your customized function 'sort' won't be called. Maybe the standard library 'sort' is called and overrides your own 'sort' function. Would you please check this? if you changed the name of your function 'sort', then yours will be called!


    2: Would you please check why my code below won't work?

    • nascardriver

      1: This was an interesting one. If we look at this strictly, we can say <algorithm> was not included, so `std::sort` isn't visible and won't be called. This is true in msvc's standard library, but gcc and clang have an include to <algorithm> hidden inside one of the headers (Or deeper) we included in the snippet.
      When `std::sort` is visible, it can be used without the `std::prefix`, because we passed it `std::greater`, which lives in the `std` namespace (Functions are looked up in the namespaces which their arguments live in, this is called argument depended lookup).
      The compiler now sees `sort` and `(std::)sort` and has to decide which on is the better match. Our `sort` wants a `std::function`, but we pass it `std::greater`, so it would have to perform 1 conversion. `(std::)sort` on the other hand is a template function (Covered later, works with all types) and doesn't require any conversion, which makes it a better match than our `sort`.
      This solution is quiet simple, all we have to do is disallow the lookup in the `std` namespace by using the global namespace selector. I updated the lesson accordingly. Thanks for pointing this out, I wouldn't have noticed it without you!

      2: You're overriding the elements in line 23. This is what you'd do in bubble sort, but not selection sort (Which is what you're trying to do it seems). Line 23 should be a pointer assignment, the real swapping happens in line 24.

  • function_pointers



    >>There’s a version of std::sort that takes a function as its third parameter that allows us to sort however we like. The function takes two parameters to compare, and returns true if the first argument should be ordered before the second.

    I was wondering if we could sort a list based on even numbers come first in the list and then odd numbers.Like: 90,40,80,13,99,5 (the order within even and odds doesn't matter). Because you said, "Once again, instead of writing our own custom loop functions, we can sort our array however we like in just a few lines of code!"

    >> When we use a function without parentheses (), it’s only a function pointer, not a call.

    But why the output shows different types in the code below, if both "foo" (without parentheses) and  "fcnPtr" are function pointers:

    int __cdecl(int)
    int (__cdecl*)(int)

    • nascardriver

      Q1: Your `greater` cannot be used for `std::sort` (It probably compiles, but is illegal). See the requirements for the compare function on cppreference
      Particularly, `greater(3, 5)` has to return a different value than `greater(5, 3)` and `greater(3, 3)` has to be `false`.
      What you need is `std::partition`

      Q2: Functions are different from function pointers. But when function are used without the parentheses, they're usable as function pointers.

  • sAmIra


    1) Does std::find_if, pass the deference 'begin' (*begin) to function foundString behind the scenes and does std::find_if has a built-in loop inside it?

    2) Is the following correct to find all strings has substring 'nut'?

    3) please ignore the post below. It was too late to edit the post.


  • sAmIra

    1) Does std::find_if, pass the *begin to function foundString behind the scenes?

    2) Is the following correct to find all strings has substring 'nut'?

    • nascardriver

      1) `std::find_if` passes every element from `begin` to `end` (exclusive) to `foundString`.
      2) It works, but is extremely inefficient and prints nuts multiple times ("apple", "banana", "walnut" prints "walnut" 3 times). You can use `std::views::filter`

      If your compiler doesn't support ranges yet, you can use a regular for loop and manually call `foundString`.

      • sAmIra

        Thanks for your reply and the solution.

        >>>`std::find_if` passes every element from `begin` to `end` (exclusive) to `foundString`.
        So, in that case, does std::find_if has a built-in loop inside it?How std::find_if passes each element?

        >>> It works, but is extremely inefficient and prints nuts multiple times ("apple", "banana", "walnut" prints "walnut" 3 times). You can use `std::views::filter`

        The solution for this post is wrong. I modified it and because it was too late to edit the post, I posted the new comment(above this comment)where 'nuts' won't print multiple times

        I got compile-error while running your code: (using C++20)

        'filter' is not a member of 'std'
        'view' the symbol left of '::' must be a type
        'filter' identifier not found

  • sAmIra

    >> but there are many more, and we encourage you to read through the linked reference to see everything that’s available!

    May I know where this linked reference is?

  • Mohammad

    Why is the following program so weird?

    The output is somewhat fantastic.
    In my compiler, it printed:
    Enter something:
    ikra was my input, But the output is iark not arki. What's the reason?
    Sorry to disturb, but advanced thanks to both of you.

    • nascardriver

      Disable compiler extensions. Your code is not valid C++. Use `std::string`.

      Once you do that, you'll find that your output is still messed up, because you're printing characters in line 26 and 44 without printing line feeds anywhere.

      • Mohammed

        So can you tell me a way that the user inputs a string and I will tell the user how many vowels he inputted?
        What are line feeds? Refer me a lesson in this case.

        • nascardriver

          Use an `std::string` to store the string. If you want to allow spaces, use `std::getline` to read the input, otherwise use `std::cin >>`.
          You `validate` function already does what you need. You can let all but 1 cases fall through, and remove the `break`s. A `return` stop everything. So you'll only have 1 `return true` and 1 `return` false in that function.
          Then use `std::ranges::count_if` (or `std::count_if` before C++20) with your string as the container and `validate` as the function.

          If you need help with any of this, please ask.

  • Mohammad

    So welcome Nascardriver and Alex. My plan is to write a program such that I can get any user input but I will just omit all the blank spaces and symbols, only English alphabets will be there, but the output will be the reverse one of the input. So I tried this one but I'm stuck. I took help from other books along with your tutorials. But how do I pass the string characters to the function valid and get the length of the string, I don't know.

    Would you please help me this regard? There could a thousand mistakes,but I am trying to learn it now.
    Sorry to disturb you but thanks for those awesome tutorials.

    • nascardriver

      I'm not sure I understand what you want to do. From what it sounds like, all you need is

      You can enter anything, and whitespace acts as a delimiter.

  • Alek

    Hey,in previous lessons there were another way to sort arrays in descending order I wonder whichone is preferred ?

    • nascardriver

      The first version should work with more containers. Most containers define `begin` and `end`, but few define `rbegin` and `rend`.

  • Ambareesh

    What is meant exactly by this line?

    "std::for_each allows the loop-body to be re-used and it can be parallelized"

    Since we are modifying the original array,  how is "re-use" made possible?

    • Thomas Kennings

      I think he meant that the function that is called in std::for_each can be used elsewhere. Were we to write our own loop, the code inside the loop couldn't be called elsewhere.

    • nascardriver

      A week has passed and I still don't remember what I had in mind when writing this. Although what Thomas said is correct, we could do the same with a range-based for-loop and a separate function, or use `std::for_each` with a lambda (Covered later. A function that's defined inside the call to `std::for_each`).
      I removed the "reuse" part from the lesson and instead showed how `std::for_each` can be safer than, or superior to, a range-based for-loop.

  • Jimmy Hunter

    Like earlier posts about function greater, I am also confused.
    We call function greater without passing any arguments to it.

        std::sort(arr.begin(), arr.end(), greater);

    Yet in the function declaration we have two parameters which are matched with no arguments.

        bool greater(int a, int b)

    I read answers to the other related posts and am still confused.  Maybe chapter 7 will explain it?

  • Jimmy Hunter

    Typos in the Tip code comments

    Typo = caparison
    Correction = comparison


    Typo = capari
    Correction = compar

  • Jimmy Hunter

    Typo = functionailty
    Correction = functionality

    Typo = The std::find_if function work similarly to std::find,
    Correction = The std::find_if function works similarly to std::find,

    Typo = std::for_each takes a list as input, applies a custom function to every element.
    Correction = std::for_each takes a list as input and applies a custom function to every element.

  • frequent_visitor

    We have learned different types of sorting, like insertion sort and bubble sort. But we haven't learnt about how the std::sort() function exactly works. Hence, when there is code like this :

    I can assume that in the ContainsNut function, each iterator that is being traversed(by find_if) is being checked for the condition, so therefore there is no need of an input argument to the function ContainsNut(in the parameters of std::find_if).

    But, in the code below, where there is sorting. Our greater function has two input arguments that are a and b. We are not giving it any inputs, then how exactly is it working. I checked out section 6.17 and 6.8a  Q2 but still cannot understand how its running:

    • nascardriver

      We explain how functions get passed as arguments and called in chapter 7. `std::find_if` and `std::sort` call the function and pass elements of the container as arguments. The C++ standard is intentionally vague when it defines algorithms. It defines what the function does, but not how. That means that `std::sort` can use whatever sorting implementation it considers to be best.

  • p1

    I don't understand how do the bool functions above work.What do they take in as arguments and how does the returned value(true or false) change the sorting ?

    • nascardriver

      In lesson 6.4, there's an example of selection sort. In that example, line 21

      would be replaced with

  • salah

    what is wrong witht that?

    it's expected to print a bool, but i got an error saying
    {No operator '=' matches these operands}

    • nascardriver

      You're missing "std::" before `cout`. You capitalized "String" in "std::String". You're missing parentheses around the comparison.
      `<<` is executed first. You're printing `std::string::npos` and then comparing `std::cout != name.find("sa")`.

  • salah

    Hi, I was wondering what is the arguments of our function will be : in this line for instance,

    arr.begin and arr.end return pointers to the first and the spot beyond the last element in the array... And what happens after that ? I mean how the pointers will be converted into int and be passed to the array?

  • In paragraph 5, it refers to the "reference linked above" - what reference?  There are no links above this on the page.

  • paprika

    std::greater should be include type declaration just like (std::greater<int>) in C++ 11.

  • kavin

    Hi, i can't understand how this works?

    what does the returned std::string_view::npos do?

    Also under std::sort , we have 2 parameters in

    How do we determine how many parameters to take ?(because so far we have taken 1 parameter for 1 argument...) I see function names "containsNut/doubleNumber". Which ones are the exact arguments to consider for the resp functions ?

    • nascardriver

      `std::string_view::find` returns the index at which the substring was found. If it doesn't find the substring, it returns the special value `std::string_view::npos` (Similarly, `std::string::find` returns `std::string::npos`).

      `std::sort` uses the function to compare 2 elements to each other. If it took only one argument, it wouldn't be able to compare it to anything. You can look this up on cppreference (We're using the version marked with (3)).

  • Ged

    I wrote a code when the user has to put the input. We cannot input std::string_view. I wrote the function inside the std::find_if just to know it is possible to do it that way as well.

    Question 1 Is the code above a good solution to the problem i wrote?

    Question 2 When we write the whole function inside our algorithm like std::find_if, can we do something with "[]" or does it stand for a one time use nameless function?

    Question 3 When there is a "_if" at the end, that means we need to use a function? If there is no "_if" where it is possible we need to use variable or something similar, but not a function?

    Question 4 Why don't we need to use parenthesis at the end of our function "containsNut"? EXAMPLE auto nuts{ std::count_if(arr.begin(), arr.end(), containsNut) };

    Question 5 When we are using standard library algorithms we use auto for our new variable. Is the data type is super long, is it good practice to be using auto instead of a specific data type?

    • nascardriver

      You can use `std::find_if` for any type of `std::vector`. You don't need a `std::string_view`.
      If you want to copy the `std::vector` for whatever reason, you can do so without `std::copy`:

      The rest of your code looks good :)

      That's a lambda function. Lambdas are covered in chapter 7.

      There are functions without an "_if" postfix that require a function. Those with an "_if" postfix require a function.

      We're not calling `containsNut`, `std::count_if` is calling `containsNut`. We're passing the functions itself, not the return value, to `std::count_if`. Function pointers are also covered in chapter 7.

      If the type is obvious, very long, or doesn't matter, use `auto`. For example in the lambda you're passing to `std::find_if`, it's obvious that the parameter has the type of the vector's elements, so you can use `auto` without sacrificing readability.

  • Matt

    I just had to :)  Glad to see this site is still being updated!

    • nascardriver

      You don't even need to write a function do print lists:

      We're using loops in the lessons, because they're easier for beginners. Maybe we'll add some single-line action in later lessons :)

Leave a Comment

Put all code inside code tags: [code]your code here[/code]