Why any! with map! is not working here

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Jun 6 09:49:28 UTC 2019


On Thursday, June 6, 2019 3:01:11 AM MDT rnd via Digitalmars-d-learn wrote:
> I am trying to check if any character in the string is > 127 by
> following function:
>
> import std.algorithm.searching;
> import std.algorithm.iteration;
> bool isBinary(char[] ss){
>    return (any!(map!(a => a > 127)(ss)));
> }
>
> However, I am getting this error:
>
> Error: variable ss cannot be read at compile time
>
> I also tried:
>
> bool isBinary(char[] ss){
>   return (any!(ss.map!(a => a > 127)));
> }
>
>
> But I get error:
>
> Error: template identifier map is not a member of variable
> mysrcfile.isBinary.ss
>
>
> How can this be corrected?

Well, it might be easier to see the problem if you removed the outer layer
of parens, since they do nothing and just clutter up the code further.

bool isBinary(char[] ss){
   return any!(map!(a => a > 127)(ss));
}

Now, let's add the parens for the function call for clarity.

bool isBinary(char[] ss){
   return any!(map!(a => a > 127)(ss))();
}

So, as should now be obvious, the first problem here is that you're not
passing any function arguments to any - neither through UFCS or through the
normal function call syntax. any takes a predicate as a template argument
and a range as a runtime argument. Presumably, you want to be passing ss to
any. However, you're passing it to map, which is part of the predicate for
any, and the way that you're doing it results in the compiler trying to
evaluate ss at compile-time, which isn't going to work, because it's a
runtime variable - hence the compiler error that you're seeing.

So, to start, the any portion should be something more like

any!pred(ss);

or

ss.any!pred();

or

ss.any!pred;

where pred is whatever the predicate is. That would then iterate through ss
and return whether the predicate returned true for any element. So, you'd
want something like

bool isBinary(char[] ss){
   return any!pred(ss);
}

That then leaves the question of what the predicate should be (since, you're
obviously not going to just put the value pred there). The predicate needs
to be something that's callable - be it a function, a delegate, a lambda,
etc. Most typically, it's a lambda.

map!(a => a > 127)(ss);

is not a lambda. It's simply a function call to map. The a => a > 127 is a
lambda for map, but the overall expression is not a lambda. It calls map on
ss and converts it to a range of bool where each element is true if the
corresponding element of ss was greater than 127. It could be turned into a
lambda by doing something like

b => map!(a => a > 127)(ss)

but the return value isn't bool (which is what's necessary for a predicate),
and it really doesn't make sense to be using ss in the predicate anyway.
You're already iterating over ss with any. What you want is a predicate
that's going to be true for an element of ss that matches the condition
you're testing for. What you said about that is

> I am trying to check if any character in the string is > 127

So, let's build something that does that. The simplest would be

a => a > 127

This would then give you

bool isBinary(char[] ss){
   return any!(a => a > 127)(ss);
}

map is neither necessary nor useful here. map is for converting the elements
from one type to another, which you don't need to do here.

Now, that solution _does_ unfortunately autodecode, so it's actually
decoding sequences of char to dchar and then testing them - which will work
in this case but is unnecessary work. To avoid that, either
std.string.representation or std.utf.byCodeUnit would work. e.g.

bool isBinary(char[] ss){
   return any!(a => a > 127)(ss.representation());
}

or

bool isBinary(char[] ss){
   return any!(a => a > 127)(ss.byCodeUnit());
}

representation would convert string[] to immutable(ubyte)[], and byCodeUnit
converts string[] to a range of code units instead of a range of code points
(so a range of char instead of a range of dchar).

Of course, you could also do something like

bool isBinary(char[] ss){
   return any!isASCII(ss.byCodeUnit());
}

using std.ascii.isASCII, which makes what you're testing for more explicit.
And if you prefer UFCS, then

bool isBinary(char[] ss){
   return ss.byCodeUnit().any!isASCII();
}

or

bool isBinary(char[] ss){
   return ss.byCodeUnit.any!isASCII;
}

- Jonathan M Davis





More information about the Digitalmars-d-learn mailing list