How to apply step when iterating over string characters? - rust

Is there an easy way to apply a step during iteration? I have seen a reference to step_by() in the book but I cant seem to get it to work.
For example, to print every-other character of a string I can do this but is there an easier way?
let s1 = "whhaatt".to_string();
for letter in s1.chars().enumerate() {
let (i, l) = letter;
if i % 2 == 0 {
println!("{:?}", l );
}
}

The simplest way would be to use the step adaptor from the itertools crate. In this case, you could use s1.chars().step(2).
Aside: Your code does not iterate over "characters"; it iterates over code points. It's quite likely that you want the graphemes method from the unicode-segmentation crate.

Related

Removing nested for loops

I am currently working on a project, in which I am referencing a grid quite often. This grid uses both X and Y axis. When I want to iterate through the grid I use two nested for loops, one for each axis.
for x in range 0..10
{
for y in range 0..20
{
}
}
I was wondering if there was any way to make this more neat / easier to read by using only one line of code. To look more like...
for x in range 0..10 && y in range 0..20
{
}
This would generally help the readability. This obviously assumes there would be no code in just the x loop.
As others pointed out, .flat_map() is the way to go to avoid writting nested expressions in a classical way, and without adding external deps to your project.
fn main() {
for (i, j) in (0..10).flat_map(|i| (0..20).map(move |j| (i, j))) {
// do job here
}
}

Is there a range expression that can be used to refer to the last N elements of a slice?

I have a slice of unknown length and I would like to try get a slice to the last N elements of that slice. The only way I can think of doing this is as follows (e.g., for 4 elements):
if let [.., a, b, c, d] = my_slice {
//...
}
This feels very cumbersome though. I would have thought that one of the range expressions would have provided this functionality, but none of them seem to do so... Is there another way of doing this? Ideally, I would be able to work with a slice and not the individual values a, b, c, d in my example above.
I like your solution, but you could use get. A simple version looks like this:
fn last4<T>(slc: &[T]) -> Option<&[T]> {
slc.get(slc.len()-4..slc.len())
}
In practice, you'd need to check that slc.len() is at least 4.
See it in action: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2c9472aea45cf23b1f46b54de7132cd7.

What is the simplest way to split a string into a list of characters?

This appears to be covered by the Str module in the api documentation but according to this issue opened this is an oversight.
This is perhaps the simplest, though certainly not the most efficient:
let split = s =>
s |> Js.String.split("")
|> Array.to_list
|> List.map(s => s.[0])
This is more efficient, and cross-platform:
let split = s => {
let rec aux = (acc, i) =>
if (i >= 0) {
aux([s.[i], ...acc], i - 1)
} else {
acc
}
aux([], String.length(s) - 1)
}
I don't think it usually makes much sense to convert a string to a list though, since the conversion will have significant overhead regardless of method and it'd be better to just iterate the string directly. If it does make sense it's probably when the strings are small enough that the difference between the first and second method matters little.

Short-circuiting in functional Groovy?

"When you've found the treasure, stop digging!"
I'm wanting to use more functional programming in Groovy, and thought rewriting the following method would be good training. It's harder than it looks because Groovy doesn't appear to build short-circuiting into its more functional features.
Here's an imperative function to do the job:
fullyQualifiedNames = ['a/b/c/d/e', 'f/g/h/i/j', 'f/g/h/d/e']
String shortestUniqueName(String nameToShorten) {
def currentLevel = 1
String shortName = ''
def separator = '/'
while (fullyQualifiedNames.findAll { fqName ->
shortName = nameToShorten.tokenize(separator)[-currentLevel..-1].join(separator)
fqName.endsWith(shortName)
}.size() > 1) {
++currentLevel
}
return shortName
}
println shortestUniqueName('a/b/c/d/e')
Result: c/d/e
It scans a list of fully-qualified filenames and returns the shortest unique form. There are potentially hundreds of fully-qualified names.
As soon as the method finds a short name with only one match, that short name is the right answer, and the iteration can stop. There's no need to scan the rest of the name or do any more expensive list searches.
But turning to a more functional flow in Groovy, neither return nor break can drop you out of the iteration:
return simply returns from the present iteration, not from the whole .each so it doesn't short-circuit.
break isn't allowed outside of a loop, and .each {} and .eachWithIndex {} are not considered loop constructs.
I can't use .find() instead of .findAll() because my program logic requires that I scan all elements of the list, nut just stop at the first.
There are plenty of reasons not to use try..catch blocks, but the best I've read is from here:
Exceptions are basically non-local goto statements with all the
consequences of the latter. Using exceptions for flow control
violates the principle of least astonishment, make programs hard to read
(remember that programs are written for programmers first).
Some of the usual ways around this problem are detailed here including a solution based on a new flavour of .each. This is the closest to a solution I've found so far, but I need to use .eachWithIndex() for my use case (in progress.)
Here's my own poor attempt at a short-circuiting functional solution:
fullyQualifiedNames = ['a/b/c/d/e', 'f/g/h/i/j', 'f/g/h/d/e']
def shortestUniqueName(String nameToShorten) {
def found = ''
def final separator = '/'
def nameComponents = nameToShorten.tokenize(separator).reverse()
nameComponents.eachWithIndex { String _, int i ->
if (!found) {
def candidate = nameComponents[0..i].reverse().join(separator)
def matches = fullyQualifiedNames.findAll { String fqName ->
fqName.endsWith candidate
}
if (matches.size() == 1) {
found = candidate
}
}
}
return found
}
println shortestUniqueName('a/b/c/d/e')
Result: c/d/e
Please shoot me down if there is a more idiomatic way to short-circuit in Groovy that I haven't thought of. Thank you!
There's probably a cleaner looking (and easier to read) solution, but you can do this sort of thing:
String shortestUniqueName(String nameToShorten) {
// Split the name to shorten, and make a list of all sequential combinations of elements
nameToShorten.split('/').reverse().inject([]) { agg, l ->
if(agg) agg + [agg[-1] + l] else agg << [l]
}
// Starting with the smallest element
.find { elements ->
fullyQualifiedNames.findAll { name ->
name.endsWith(elements.reverse().join('/'))
}.size() == 1
}
?.reverse()
?.join('/')
?: ''
}

OCaml : need help for List.map function

I need to create a function that basically works like this :
insert_char("string" 'x') outputs "sxtxrxixnxg".
So here is my reasoning :
Create a list with every single character in the string :
let inserer_car(s, c) =
let l = ref [] in
for i = 0 to string.length(s) - 1 do
l := s.[i] :: !l
done;
Then, I want to use List.map to turn it into a list like ['s', 'x', 't', 'x' etc.].
However, I don't really know how to create my function to use with map. Any help would be appreciated!
I'm a beginner in programming and especially in ocaml! so feel free to assume I'm absolutely ignorant.
If you were using Core, you could write it like this:
open Core.Std
let insert_char s c =
String.to_list s
|> (fun l -> List.intersperse l c)
|> String.of_char_list
Or, equivalently:
let insert_char s c =
let chars = String.to_list s in
let interspersed_chars = List.intersperse chars c in
String.of_char_list interspersed_chars
This is just straightforward use of existing librariies. If you want the implementation of List.intersperse, you can find it here. It's quite simple.
A map function creates a copy of a structure with different contents. For lists, this means that List.map f list has the same length as list. So, this won't work for you. Your problem requires the full power of a fold.
(You could also solve the problem imperatively, but in my opinion the reason to study OCaml is to learn about functional programming.)
Let's say you're going to use List.fold_left. Then the call looks like this:
let result = List.fold_left myfun [] !l
Your function myfun has the type char list -> char -> char list. In essence, its first parameter is the result you've built so far and its second parameter is the next character of the input list !l. The result should be what you get when you add the new character to the list you have so far.
At the end you'll need to convert a list of characters back to a string.

Resources