Michael S
2024-09-25 16:54:23 UTC
On Wed, 25 Sep 2024 17:14:09 +0200
lambdas, but that really should be a new thread.
Some time ago on comp.lang.c we had very long thread about floodfill4
algorithm (that both myself and TimR took more seriously than an
issue deserves, but that's off topic).
Today I coded two implementations of original brute-force recursive
algorithm.
// floodfill_recursive_nested.
// Implementation is in none-tricky C++
// Very similar to what can be done in C
#include <cstddef>
int floodfill4(
unsigned char *grey,
int width, int height,
int x, int y,
unsigned char target, unsigned char dest)
{
if (width < 1 || height < 1)
return 0;
if (x < 0 || x >= width || y < 0 || y >= height)
return 0;
size_t w = width, h = height;
if (grey[y*w+x] != target)
return 0;
struct {
unsigned char *grey;
size_t width, height;
unsigned char target, dest;
void core(size_t x, size_t y) const
{
if (x < width && y < height) {
auto idx = y*width+x;
if (grey[idx] == target) {
grey[idx] = dest;
core(x - 1, y);
core(x + 1, y);
core(x, y - 1);
core(x, y + 1);
}
}
}
} context = {
.grey = grey,
.width = w, .height = h,
.target = target, .dest = dest,
};
context.core(x, y);
return 1;
}
// end of floodfill_recursive_nested.
// floodfill_recursive_lambda.
// Implementation in tricky C++
// C can not do it
#include <cstddef>
int floodfill4(
unsigned char *grey,
int width, int height,
int x, int y,
unsigned char target, unsigned char dest)
{
if (width < 1 || height < 1)
return 0;
if (x < 0 || x >= width || y < 0 || y >= height)
return 0;
size_t w = width, h = height;
if (grey[y*w+x] != target)
return 0;
auto core = [=] (auto& a_ref, size_t x, size_t y) {
if (x >= w || y >= h)
return;
auto idx = y*w+x;
if (grey[idx] == target) {
grey[idx] = dest;
a_ref(a_ref, x - 1, y);
a_ref(a_ref, x + 1, y);
a_ref(a_ref, x, y - 1);
a_ref(a_ref, x, y + 1);
}
};
core(core, x, y);
return 1;
}
// end of floodfill_recursive_lambda.
In the second variant in order to make it compile at all I had to uses
very dirty trick with lambda passed as parameter to itself. I copied it
from Stack Overflow, but don't pretend to understand why it works and
why it needed in the first place.
But that's only part of the story.
The other part is that the first variant is 1.2x faster.
On Wed, 25 Sep 2024 15:04:27 +0200
a lambda himself. That's pre-C++11-style and maybe his knowledge
is from then.
Seriously? Sometimes I wonder if you ever bother reading other
people's posts.
Of /course/ Michael knows he could use a lambda there.
[O.T.]
I know that they can be used, but I never use lambdas myself.
Because of that my knowledge is theoretical and I am not fluent with
syntax.
Why I am not using lambdas myself? Because I think that lambda with
captures makes code harder to follow and to understand (that's my
1st hand experience from reading big corpus of Ada83 code. Ada83
does not have lambdas, but it has nested procedures that can access
parent's variable, similarly to lambda with [&] default capture).
And lambda without captures does not provide enough of advantage
over named functions to bother with mastering new concept.
It's maybe worth having another thread about the pros and cons ofThat's reasonable if you have something useful to say. Telling
people that they can use lambdas here is /not/ useful. We all
know we could use lambdas, but that is totally missing the point
of the discussion.
I guess Michael S doesn't know this, otherwrite he would have usedpeople that they can use lambdas here is /not/ useful. We all
know we could use lambdas, but that is totally missing the point
of the discussion.
a lambda himself. That's pre-C++11-style and maybe his knowledge
is from then.
people's posts.
Of /course/ Michael knows he could use a lambda there.
I know that they can be used, but I never use lambdas myself.
Because of that my knowledge is theoretical and I am not fluent with
syntax.
Why I am not using lambdas myself? Because I think that lambda with
captures makes code harder to follow and to understand (that's my
1st hand experience from reading big corpus of Ada83 code. Ada83
does not have lambdas, but it has nested procedures that can access
parent's variable, similarly to lambda with [&] default capture).
And lambda without captures does not provide enough of advantage
over named functions to bother with mastering new concept.
lambdas, but that really should be a new thread.
algorithm (that both myself and TimR took more seriously than an
issue deserves, but that's off topic).
Today I coded two implementations of original brute-force recursive
algorithm.
// floodfill_recursive_nested.
// Implementation is in none-tricky C++
// Very similar to what can be done in C
#include <cstddef>
int floodfill4(
unsigned char *grey,
int width, int height,
int x, int y,
unsigned char target, unsigned char dest)
{
if (width < 1 || height < 1)
return 0;
if (x < 0 || x >= width || y < 0 || y >= height)
return 0;
size_t w = width, h = height;
if (grey[y*w+x] != target)
return 0;
struct {
unsigned char *grey;
size_t width, height;
unsigned char target, dest;
void core(size_t x, size_t y) const
{
if (x < width && y < height) {
auto idx = y*width+x;
if (grey[idx] == target) {
grey[idx] = dest;
core(x - 1, y);
core(x + 1, y);
core(x, y - 1);
core(x, y + 1);
}
}
}
} context = {
.grey = grey,
.width = w, .height = h,
.target = target, .dest = dest,
};
context.core(x, y);
return 1;
}
// end of floodfill_recursive_nested.
// floodfill_recursive_lambda.
// Implementation in tricky C++
// C can not do it
#include <cstddef>
int floodfill4(
unsigned char *grey,
int width, int height,
int x, int y,
unsigned char target, unsigned char dest)
{
if (width < 1 || height < 1)
return 0;
if (x < 0 || x >= width || y < 0 || y >= height)
return 0;
size_t w = width, h = height;
if (grey[y*w+x] != target)
return 0;
auto core = [=] (auto& a_ref, size_t x, size_t y) {
if (x >= w || y >= h)
return;
auto idx = y*w+x;
if (grey[idx] == target) {
grey[idx] = dest;
a_ref(a_ref, x - 1, y);
a_ref(a_ref, x + 1, y);
a_ref(a_ref, x, y - 1);
a_ref(a_ref, x, y + 1);
}
};
core(core, x, y);
return 1;
}
// end of floodfill_recursive_lambda.
In the second variant in order to make it compile at all I had to uses
very dirty trick with lambda passed as parameter to itself. I copied it
from Stack Overflow, but don't pretend to understand why it works and
why it needed in the first place.
But that's only part of the story.
The other part is that the first variant is 1.2x faster.