"Stack overflow during evaluation" with stdlib List.map - functional-programming

Say I have a bunch of ones:
Array.make (int_of_float (2. ** 17.)) 1
|> Array.to_list;;
- : int list
= [1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1;
1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; 1; ...]
And I want to map a function over these ones:
Array.make (int_of_float (2. ** 17.)) 1
|> Array.to_list
|> List.map (fun x -> x * 2);;
Stack overflow during evaluation (looping recursion?).
Seems like it's too much! Looking into how List.map is implemented in Ocaml, I find this
(https://github.com/ocaml/ocaml/blob/trunk/stdlib/list.ml#L57-L59):
let rec map f = function
[] -> []
| a::l -> let r = f a in r :: map f l
I'm pretty new to Ocaml, but it looks like map is written in a way that makes it not tail-recursive.
Is that right?
How do I map a function over lots of stuff?

Yes, List.map in OCaml Standard Library is not tail-recursive. You have several options here:
Use Array.map
Use List.rev_map (possibly coined with List.rev)
Use some other data structure that is more suitable to your task
Don't use standard library
While the first two options are obvious, the latter two need some explanation.
Better data structure
If you really have a big amount of data, and if this data is numerical, then you should consider using Bigarrays. Also, depending on you use case, Maps and Hashtables might be better. People in functional programming languages tend to use lists everywhere, instead of choosing a proper data structure. Don't step into this pitfall.
Other libraries and why it is non tail recursive
The List.map is non tail recursive for a good reason. There is always a tradeoff between stack usage and performance. For small lists (and this is the most common use case) a non-tail recursive version is much faster. So the standard library decided to provide a fast List.map and in case if you need to work on big lists then you can use List.rev_map xs |> List.rev. Moreover, sometimes you can omit the List.rev part. So, the standard library, is not trying to think for you, it gives you a choice.
On the other hand, with time people managed to find some optimum in this tradeoff, i.e., having a constant stack version which is quite fast. The solution is to use a non tail recursive approach when a list is small, and then fallback to a slow version. Such implementations are provided by Janestreet Core Library, Batteries and Containers library.
So, you can switch to those libraries and forget about this issue. Though it is still highly advised to use List.map scarcely and with care, as this operation is always has linear memory consumption (either heap or stack memory), that can be avoided. So, it is better to use rev_map whenever it is possible.

You are correct List.map is not tail recursive , use List.rev_map instead which is tail recursive. ... And List.rev that should be tail recursive.
List documents all the function of List module, indicating whether it is not tail recursive.

What you want to do is construct the list at the end of your calculation:
let construct_value _ =
1 |> (fun x -> x * 2)
let rec construct_list f n =
let rec aux acc n =
if n < 0
then acc
else aux (f n) (n-1)
in
aux [] (n-1)
let result = construct_list construct_value (int_of_float (2. ** 17.))
Here you end up with the same list as in your calculations, but with way less time and memory usage. Note that the elements of the list are constructed from the bottom of the list (you have to do it for tail-recursion), I added an "index" parameter that corresponds to the exact position in your array (that I did not construct, another time and memory gain)
Also, if you want to do calculations on that big of a data structure, lists are probably not the best option, but that depends on what you intend to do with it after.

Related

Using SCSS variables as css keyframe percentages

#mixin fade($num:1, $fade:1, $visible:2) {
#keyframes fade {
0% { opacity: 0; }
#{ $a }% { opacity: 1; }
#{$a * ($fade + $visible)}% { opacity: 1; }
#{$a * ($fade + $visible + $fade)}% { opacity: 0; }
100% { opacity: 0; }
}
}
Im getting the error } expectedscss(css-rcurlyexpected)
It works if i just type the percentages so I think its something to do with how im substituting the variables in
You are converting your number to a percentage improperly.
#mixin fade($num:1, $fade:1, $visible:2) {
#keyframes fade {
0% { opacity: 0; }
#{($num)}% { opacity: 1; }
#{($num * ($fade + $visible))}% { opacity: 1; }
#{($num * ($fade + $visible + $fade))}% { opacity: 0; }
100% { opacity: 0; }
}
}

How to make transition CSS rule on img src change

I'm making my React app and I made an image carousel using setInterval which changes img src every 2 seconds.
Now I'd like to animate the change of img using transition. (CSSTransitionGroup is unclear to me).
However I have a problem with attaching transition to any measureable element. Can I somehow "grab" the setInterval source-changing event using css?
So far, I've tried adding "opacity: 1" style to images set to opacity: 0, but the opacity: 1 stays beyonod every iteration of interval, thus only the first image gets transition and the new ones have a "opacity:1" styling by default.
I'm talking about the " NavLink to ="/about_us"> " section
Here's my code. Any ideas? I've wasted 5hours on this:
import React, {
Component
} from 'react';
import {
NavLink
} from "react-router-dom";
class Radzikowskiego_S2_Staff extends Component {
constructor(props) {
super(props);
this.state = {
spanText: 'Zabawy',
text: ["Rozwoju", "Odpoczynku", "Śmiechu", "Zabawy"],
staffImg: "Aleksandra_Brozyna",
staff: ["Dominika_Serwa", "Dorota_Szastak", "Joanna_Wozniak", "Alicja_Kwasny", "Kinga_Kaczmarek", "Monika_Garula", "Maria_Kaczmarek", "Natalia_Kiczura", "Violetta_Wojas"],
index: 0,
indexStaff: 0,
}
}
componentDidMount() {
this.changeSpan();
this.changeStaff();
}
changeSpan = () => {
this.interval = setInterval(() => {
this.setState({
spanText: this.state.text[this.state.index],
index: this.state.index >= this.state.text.length - 1 ? 0 : this.state.index + 1
});
}, 2000);
};
changeStaff = () => {
this.interval = setInterval(() => {
this.setState({
staffImg: this.state.staff[this.state.indexStaff],
indexStaff: this.state.indexStaff >= this.state.staff.length - 1 ? 0 : this.state.indexStaff + 1,
});
}, 2000);
};
showImg = () => {
// console.log(event.target.style.opacity='0.5');
// console.log(this.refs.img_src.style.opacity='1')
this.refs.img_src.classList.add('show_images')
};
componentWillUpdate() {
this.showImg();
this.removeImg();
}
removeImg = () => {
// console.log(event.target.style.opacity='0.5');
// console.log(this.refs.img_src.style.opacity='1')
// console.log(this.refs.img_src.classList)
this.refs.img_src.classList.remove('show_images')
};
render() {
return (
<
div id = 'staff' >
<
div className = 'row' >
<
div className = 'col-12 text-center mb-3' >
<
h3 > Krakowiaczek to miejsce do </h3> <
h6 id = "staff_span"
className = "animate_span" > {
this.state.spanText
} < /h6> <
h6 class = "mt-3" > W Przedszkolu Krakowiaczek nie ma czasu na nudę. < /h6>
<
/div> <
/div>
<
div class = 'row align-items-center ' >
<
div className = 'col-md-2 col-md-offset-1 section_2_thumbnail' >
<
NavLink to = "/our_philosophy" >
<
img src = 'images/filozofia.svg'
className = 'section_2_thumbnail_img' / >
<
p class = "pt-2" > Nasza Filozofia < /p> <
/NavLink> <
/div> <
div className = 'col-md-2 col-md-offset-1 section_2_thumbnail' >
<
NavLink to = "/extended_offer" >
<
img src = 'images/what_we_offer.svg'
className = 'section_2_thumbnail_img' / >
<
p className = "pt-2" > Co oferujemy < /p> <
/NavLink> <
/div> <
div className = 'col-md-2 col-md-offset-1 section_2_thumbnail' >
<
NavLink to = "/enrollment" >
<
img src = 'images/zapisy.svg'
className = 'section_2_thumbnail_img' / >
<
p className = "pt-2" > Zapisy < /p> <
/NavLink> <
/div>
<
div className = 'col-md-3 col-md-offset-1 section_2_thumbnail' >
<
NavLink to = "/about_us" >
<
img src = {
`images/teachers/${this.state.staffImg}.jpg`
}
className = 'section_2_thumbnail_staff pb-2'
id = 'staffIcon'
onLoad = {
this.showImg
}
ref = 'img_src' / >
<
p className = "mt-2 mb-3" > Kadra < /p> <
/NavLink> <
/div> <
/div>
<
/div>
)
}
}
export default Radzikowskiego_S2_Staff;
Ok, I used the crossfade functionality to get the issue done. I used map to render all img srcs from an array and then used animation to generate the image shift.
I think I'll incorporate some sort of sass loop to simplify the scss once I do some refactor although a little tip would be nice
<div class="staff_img_wrapper">
{
this.state.staff.map(element => {
return (
<img src={`images/teachers/${element}.jpg`} className='section_2_thumbnail_staff pb-2' id='staffIcon' />
)
})
}
</div>
CSS
.staff_img_wrapper {
height: 20vh;
img:nth-child(1) {
position: absolute;
opacity: 1;
left: 0;
right: 0;
margin: 0 auto;
animation: fadeOut 3s;
animation-fill-mode: forwards;
z-index:10;
}
img:nth-child(2) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 3s;
animation-fill-mode: forwards;
z-index: 9;
}
img:nth-child(3) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 6s;
animation-fill-mode: forwards;
z-index: 8;
}
img:nth-child(4) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 9s;
animation-fill-mode: forwards;
z-index: 7;
}
img:nth-child(5) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 12s;
animation-fill-mode: forwards;
z-index: 6;
}
img:nth-child(5) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 15s;
animation-fill-mode: forwards;
z-index: 5;
}
img:nth-child(6) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 18s;
animation-fill-mode: forwards;
z-index: 4;
}
img:nth-child(7) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 21s;
animation-fill-mode: forwards;
z-index: 3;
}
img:nth-child(8) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 24s;
animation-fill-mode: forwards;
z-index: 2;
}
img:nth-child(9) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 27s;
animation-fill-mode: forwards;
z-index: 1;
}
img:nth-child(10) {
position: absolute;
left: 0;
right: 0;
margin: 0 auto;
opacity: 1;
animation: fadeOut 3s;
animation-delay: 30s;
animation-fill-mode: forwards;
z-index: 0;
}
}
#keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0
}
}

How to generate an opacity helper in sass

I can't seem to generate an opacity helper classes with this code
#for $o from 1 through 9 {
.o#{$o} {
opacity: 0.#{$o};
}
}
i think it has something to do with the value of the opacity 0.#{$o};...
the code should generate:
.o10 { opacity: 0.1; }
.o20 { opacity: 0.1; }
...
.o90 { opacity: 0.9; }
Just divide by 10: opacity: $o / 10;.
#for $o from 1 through 9 {
.o#{$o} {
opacity: $o / 10;
}
}
Sassmeister demo.
Opacity as string example: opacity: unquote("0." + $o);.

CSS3 Keyframes Issue

So I am trying to get an fade in effect where I want the fade in opacity to start off from 0 and go to 1 but then when it gets played the second time I want it to start from 0.5 and get played to 1. If you need more explanation then please let me know. I have inserted the code below. Thanks
#-webkit-keyframes fadeInCustom {
0% {
opacity: 0;
}
1%{
opacity: 0.1;
}
10%{
opacity: 0.2;
}
30% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
#keyframes fadeInCustom {
0% {
opacity: 0;
}
1%{
opacity: 0.1;
}
10%{
opacity: 0.2;
}
30% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}

How to remove usage of excess keyframes by using animation-delay?

I'm looking through Google Polymer's source code and I found this:
/**
* IMPORTANT NOTE ABOUT CSS ANIMATION PROPERTIES (keanulee):
*
* iOS Safari (tested on iOS 8.1) does not handle animation-delay very well - it doesn't
* guarantee that the animation will start _exactly_ after that value. So we avoid using
* animation-delay and instead set custom keyframes for each color (as redundant as it
* seems).
*
**/
.active .spinner-layer.blue {
-webkit-animation: blue-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: blue-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.red {
-webkit-animation: red-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: red-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.yellow {
-webkit-animation: yellow-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: yellow-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
.active .spinner-layer.green {
-webkit-animation: green-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
animation: green-fade-in-out 5332ms cubic-bezier(0.4, 0.0, 0.2, 1) infinite both;
}
#-webkit-keyframes blue-fade-in-out {
from { opacity: 1; }
25% { opacity: 1; }
26% { opacity: 0; }
89% { opacity: 0; }
90% { opacity: 1; }
100% { opacity: 1; }
}
#keyframes blue-fade-in-out {
from { opacity: 1; }
25% { opacity: 1; }
26% { opacity: 0; }
89% { opacity: 0; }
90% { opacity: 1; }
100% { opacity: 1; }
}
#-webkit-keyframes red-fade-in-out {
from { opacity: 0; }
15% { opacity: 0; }
25% { opacity: 1; }
50% { opacity: 1; }
51% { opacity: 0; }
}
#keyframes red-fade-in-out {
from { opacity: 0; }
15% { opacity: 0; }
25% { opacity: 1; }
50% { opacity: 1; }
51% { opacity: 0; }
}
#-webkit-keyframes yellow-fade-in-out {
from { opacity: 0; }
40% { opacity: 0; }
50% { opacity: 1; }
75% { opacity: 1; }
76% { opacity: 0; }
}
#keyframes yellow-fade-in-out {
from { opacity: 0; }
40% { opacity: 0; }
50% { opacity: 1; }
75% { opacity: 1; }
76% { opacity: 0; }
}
#-webkit-keyframes green-fade-in-out {
from { opacity: 0; }
65% { opacity: 0; }
75% { opacity: 1; }
90% { opacity: 1; }
100% { opacity: 0; }
}
#keyframes green-fade-in-out {
from { opacity: 0; }
65% { opacity: 0; }
75% { opacity: 1; }
90% { opacity: 1; }
100% { opacity: 0; }
}
I agree that this code looks incredibly verbose, but I wasn't sure how one would go about re-expressing it using animation-delay instead of custom keyframes.
Not all the colors seem to take a full 25% chunk. Green only takes 15%... which seems a bit odd. Nevertheless, would it be as simple as just taking the start % and multiplying it by the animation duration and setting that as the delay? How would the end-time be specified, then?
How would this code look if the bug fix did not need to be addressed?
It looks like somebody has changed the code after the initial comments.
as it is now, only 2 animations are redundant, so the comment doesn¡t make full sense.
It is a common practice in cyclic animations, where in your case 4 elements share - or should share- the same animation, but in a sequential way, to reuse the animation and change only the start time (thru the initial delay property)
In this case, that would be probably
#keyframes red-fade-in-out {
0% { opacity: 1; }
25% { opacity: 1; }
26% { opacity: 0; }
100% { opacity: 0; }
}
and then every class would have a different delay, of 1/4 of the total time of the animation.

Resources