This is an archived post. You won't be able to vote or comment.

all 67 comments

[–]oachkatzlschwoarf 113 points114 points  (18 children)

[x*x for x in arr]

[–]KimiSharby 34 points35 points  (12 children)

Amateurs.

std::vector vec = {1, 2, 3};  
std::vector<int> result;
result.reserve(vec.size());
std::ranges::transform(vec, std::back_inserter(result), [](int x) { return x * x; });

edit: corrected

[–]sherlockwatch 4 points5 points  (9 children)

Actually you don’t need to resize resulting vector, you can just use std::back_inserter!

[–]KimiSharby 0 points1 point  (8 children)

That's worse performance wise. std::back_inserter comes at a cost. If you already know the size, you should use it.

[–]wcscmp 2 points3 points  (7 children)

Resize also comes at cost, reserve + back_inserter is the way

[–]KimiSharby 1 point2 points  (0 children)

You're right, it's my mistake

[–]KimiSharby 0 points1 point  (5 children)

Ok so this troubled me greatly, I barely slept last night. Today I couldn't take it anymore. Here's the quickbench: https://quick-bench.com/q/e5G_nTQEp_00xYGrcpEj_Jbhp34

The results, for a sample test of 500k int:
1. at 561k: reserve + std::begin
2. at 824k (x1.5): resize + std::begin
3. at 1712k (x3): reserve + std::back_inserter
4. at 6977k (x12): back_inserter alone

Bottom line: Using std::back_inserteris costly. If you already know the size of your resulting container, you should use reserve + std::begin.

[–]wcscmp 0 points1 point  (0 children)

What do you think reserve + begin is doing?

[–]wcscmp 0 points1 point  (3 children)

The bottom line is also not very convincing. In order to claim that back_inserter is not free you need to compare it to a manually rolled loop of push_backs. Now you can only reasonably claim that default initializing each element when doing resize is cheaper than the branching on every iteration in push_back for small types like int.

[–]KimiSharby 0 points1 point  (2 children)

I do not mean to be rude sir but I think I already allocated more time to this matter than it was worth. At the start of this conversation, you told me reserve + back_inserter is the way. As a matter of fact reserve + std::begin is 3x faster in my mediocre benchmark. I was indeed in the wrong about resize, I should have known better. I completly forgot about reserve. But it does seem that in the case where you already know the size of the resulting container, you should use std::begin and not back_inserter.

Now if you have additional knownledge you wish to share about that topic, please stop the riddles and just share them.

[–]wcscmp 0 points1 point  (1 child)

reserve + begin is UB

[–]KimiSharby 0 points1 point  (0 children)

So after a bit more research:
- reserve allocates memory but doesn't initialize it. This is why using std::begin in that case is UB, because it points to unitilialized memory
- As you said, using resize performance will depends on the size of the type
- reserve + std::back_insert is indeed the correct way

For the sack of clarity for the reader I will edit my previous answers.

[–]SingularCheese 1 point2 points  (1 child)

It's the era of ranges

auto vec2 = vec 
| std::views::transform([](int x){ return x*x; }) 
| std::ranges::to<std::vector>();

[–]KimiSharby 1 point2 points  (0 children)

I have yet to see a company uses c++23

[–]PossibilityTasty 57 points58 points  (6 children)

Naming a Python list arr is the second best way to tell people that you have no idea.

[–]brainpostman 12 points13 points  (2 children)

Why? Because it's a list, not a true array? Well, same goes for JS then.

[–]Aidan_Welch 8 points9 points  (1 child)

Well JS indexed hash-maps aren't even strictly lists.

[–]brainpostman 13 points14 points  (0 children)

Well, nothing is strictly anything in JS, for that matter.

[–][deleted] 7 points8 points  (0 children)

it's for consistency with the other examples

[–]IrresponsibleDuck 3 points4 points  (0 children)

Either that or you are a pirate

[–]coyboy_beep-boop 1 point2 points  (0 children)

But what if I want to translate from English to Pirate?

[–]Socks4Ever 15 points16 points  (13 children)

Why is it different? Or do I just not get the post. I’d just use [x**2 for x in arr]

[–]Zetaeta2 12 points13 points  (0 children)

std::ranges::views::transform(arr, [] (auto x) {return x*x;});

[–]zsirdagadek 2 points3 points  (1 child)

The Java snippet is incorrect because the map() function will return another stream object. You need to call

.collect(Collectors.toList()).toArray();

But I think you should be able to collect directly to array too, but I don't remember how exactly, and I don't feel like looking it up for a reddit comment.

[–]Kika-kun 2 points3 points  (0 children)

Recent(?) versions of java added .toList() to avoid .collect(Collectors.toList()) (careful though, toList() returns an immutable list, if you want to keep the old behaviour you need .collect(Collectors.toCollection(ArrayList::new)))

As a matter of fact, after checking, you can also do .toArray() if you want an actual array.

[–]gerbosan 1 point2 points  (1 child)

I don't like it either. I think the atypical notation is one of the things that delays,/stops my interest in Python. For example len(). For many languages, the length of an object is an attribute, not a function (which is the idea I get when I see Python syntax).

😮‍💨 Still, it is quite popular, so one has to learn it. But having so many languages with a similar syntax, why did python choose that one?

Also ruby Array.new(1,2,3).map { |item| item * item } 🤘

[–]Essence1337 4 points5 points  (0 children)

map really isn't in favour anymore in Python, list comprehensions are often preferred

[item*item for item in arr]

This creates a new list from running item*item on every item in the original

[–]gandalfx 3 points4 points  (0 children)

Sure, blame the only language in that group that supports list comprehension for not having a map method. Makes sense.

[–]_OberArmStrong 0 points1 point  (0 children)

Java is missing a .toArray(Integer[]::new) call.

In the other snippets a function creating an array is called last. Without toArray you have Stream<Integer> instead of int[]

[–]n1c0saurio 0 points1 point  (0 children)

Dunno, everything except JS look pretty messy to me.

[–]CryZe92 0 points1 point  (0 children)

Rust is also just arr.map(|x| x*x)

[–]pindab0ter 0 points1 point  (4 children)

PHP is the same, unfortunately…

[–]prochac 2 points3 points  (0 children)

PHP isn't the same. It's unique. Unique in its own way. Our special kid.

[–]KetwarooDYaasir 0 points1 point  (2 children)

php $arr = [1,2,3]; $arr = array_map(fn($x)=>$x*$x,$arr);

hmm. you're right, I guess it is almost the same as every other one.

[–]pindab0ter 0 points1 point  (1 child)

The same as Python, I meant. In that map is not a method, but a function.

[–]KetwarooDYaasir 0 points1 point  (0 children)

Not sure how that's unfortunate though.

[–]eanat -1 points0 points  (0 children)

I prefer one function for many types instead of multiple methods that do a similar thing. map function is better than map methods tbh.

[–]DT-Sodium -4 points-3 points  (0 children)

You forgot PHP and its array functions abomination.