Jump to content

functional programming a challenge for you and a learning for me !


bexphones
 Share

Recommended Posts

hi, i'm happy to play now with short code and modularize them together.

What saves time and readability !

But in this case i don't see what it's possible to do with the function foreach.

Is it possible to use foreach like this config with a trick in the hide_weapon_internal ?

 


//normal function  who works 
if(dalle_moving[0]){
     for (var j = 0; j < dalle_moving.length; j++){
	dalle_moving[j].hide()
     }
}

////////////////////////////////////////////////////////////////////////////////////
//NOW WITH FUNCTIONAL PROGRAMATION

function is_exist(obj){
	if(typeof obj != "undefined"){
		return true; 
	}else{
		return false; 
	}
}

function foreach(table,action){
	for(var i=0;i<table.length;i++){
		action(table[i]);
	}
}

//////////////////////////////////////////
var hide_enemy=function(obj){
    is_exist(obj[0]) && foreach(obj,obj.hide)
}
hide_enemy(dalle_moving)
//////////////////////////////////////////

//obj.hide don't works because i must say action(obj[i].hide()) 
//is there a trick to do that with the function foreach as she is ?


i have the feeling that 's possible ....no?

Link to comment
Share on other sites

Couple of quick pointers:

 

1) Avoid using function names that closely match reserved words or already existing native code. 

Array.prototype.forEach

already exists, so avoid names like 'forEach'

 

2) is_exist sounds weird, and can be simplified:

const is_defined = (obj) => obj !== undefined

just keep in mind that there are better check than this, and consider cases like when obj is null, etc (be careful when doing comparisons and read on the topic)

 

3) you are trying to use a method that can be accessed by each individual item in an array. If it is always hide, you can simply do

const hide_enemies = (enemies) => enemies.forEach( (enemy) => enemy.hide() );

or if you need to call other methods, just use a string to represent a possible method name

const hide_enemies = (enemies, actionString) => enemies.forEach( (enemy) => enemy[actionString]() );

In old-school syntax, you could write

function hide_enemies(enemies){
  enemies.forEach( function(enemy){

    return enemy.hide();
  });
}

If you tell me more about why you are checking is_exist(obj[0]), we could probably avoid awkward checks.

 

Finally, you could check out this quick guide to Array functions that may provide you with more information.

Edited by ldd
added a link to provide more information
Link to comment
Share on other sites

hi,

thanks very much for these explanations.

your quick guide for array is easy to understand , thanks again ;)

9 hours ago, ldd said:

If you tell me more about why you are checking is_exist(obj[0]), we could probably avoid awkward checks.

i use an array to stock my objects.

var enemy={}

next i create the enemy  i need for each levels with a prototype and methods.

enemy[0]=new _proto_enemy()

each level have different number of enemies. level 1 have for example 2 enemies and level2 > 4, etc....

i check if enemy exist in an update method to each levels to apply method on hem like collisions with another object (player), add in the game(game.add.existing(enemy[0]), debug hem.

finally to loop trough and apply also method but with other object or methods.

Link to comment
Share on other sites

//WHY THIS NOT WORKS ?
function hide_enemies(obj,action){
	obj.forEach(function(item){
		return item.action()
	})
}

//WHY THIS WORKS ?
function hide_enemies(obj){
	obj.forEach(function(item){
		return item.hide()
	})
}

hi again :rolleyes: could you explain  why it doesn't work for the example 1 ? how must i declare "action" ? if i run hide_enemies(dalle,hide) it return the error item.action is not a function. for obj exist you have true, it's not necessary to invoke him, hide_enemies() works without because we make foreach. Thanks.

PS : i'm not yet ready for this :

const hide_enemies = (enemies, actionString) => enemies.forEach( (enemy) => enemy[actionString]() );

because i need to console.log sometimes what went wrong and in this case it's difficult.

Link to comment
Share on other sites

On 11/08/2017 at 1:29 AM, ldd said:

1) Avoid using function names that closely match reserved words or already existing native code. 

Why? There's no problem with this. Extending built-in prototypes is generally frowned upon with good arguments to back it up still, but `forEach` is not a reserved word, its just an instance member on a built-in, of which there are hundreds. If I built an event emitter should I not call one of its methods `addEventListener`? 

On 11/08/2017 at 1:29 AM, ldd said:

3) you are trying to use a method that can be accessed by each individual item in an array. If it is always hide, you can simply do


const hide_enemies = (enemies) => enemies.forEach( (enemy) => enemy.hide() );

I think the whole point of bex creating a custom for each function is for performance. (for reference, I'd do it exactly the same and just use [].forEach, at least at first).

28 minutes ago, bexphones said:

 


//WHY THIS NOT WORKS ?
function hide_enemies(obj,action){
	obj.forEach(function(item){
		return item.action()
	})
}

//WHY THIS WORKS ?
function hide_enemies(obj){
	obj.forEach(function(item){
		return item.hide()
	})
}

 

There are a couple of ways of invoking a method of an instance:

var speaker = {
  prefix: '::',
  say: function (message) {
    console.log(this.prefix, message)
  }
}

// Using dot notation to access the method
speaker.say('hello!')
// $ :: hello!

// Using square brackets and strings
speaker['say']('world!')
// $ :: world!

In your examples you're using the dot notation to invoke a function, in your first example you're trying to invoke `action` of `item` (item.action()), which probably doesn't exist.

If you want to invoke a dynamic method of an object then using the square brackets notation would work:

function callForAllEnemies (obj, method) {
  obj.forEach(function (item) {
    return item['methods]()
  })
}

I've renamed it as it is now a generic helper to call a method for all enemies, if you explicitly call the function hide_enemies then you may as well explicitly call item.hide().

Note that your custom foreach function can do this as well with some small changes:

function foreach (list, action) {
  let i = list.length
  while (--i) {
    list[i][action]()
  }
}

(As I'm assuming this function is for perf I've also used a negative counting while list, which I think is still generally the fastest array iteration method)

You can swap things around and go more generic with things too if you wanted to get in to refactoring a little:

const all = method => list => {
  let i = list.length
  while (--i) {
    list[i][action]()
  }
}

const hideAll = all('hide')
const showAll = all('show')

hideAll(enemies)

All sorts of ways to solve problems.

Link to comment
Share on other sites

9 minutes ago, mattstyles said:

You can swap things around and go more generic with things too if you wanted to get in to refactoring a little:


const all = method => list => {
  let i = list.length
  while (--i) {
    list[i][action]()
  }
}

const hideAll = all('hide')
const showAll = all('show')

hideAll(enemies)

i start with lua langage +

after i learn javascript ++

and now définitively i love FP +++

Link to comment
Share on other sites

maybe yet a question

how do you do to perform that  ?


function foreach(tableau,action){
	for(var i=0;i<tableau.length;i++){
		action(tableau[i]);
	}
}


const actions = console.log(this.name) && console.log(this.number)
foreach(dalle,actions)

and this also ?
 

function test_args(arg1,arg2){

console.log(arg1)

console.log(arg2)

}

test_args("koala","cat") //koala, cat

test args("koala") //return only koala without errors

 

Link to comment
Share on other sites

You want to specify multiple actions to your function?

There are quite a few ways to achieve this, the simplest is just to ensure that `actions` is an array and just iterate over that array of functions calling them, but, you could go a different route with variadic arguments:

function run (...args) {
  const data = args.shift()
  let i = data.length
  while (--i >= 0) {
    args.forEach(fn => fn(data[i]))
  }
}

const arr = [1, 2, 3]
run(arr, i => console.log(i))
// 3 9 2 4 1 1

Something like this should do what you want, although there are a few ways to skin this cat, see the MDN resource on function arguments.

Note that this uses the spread syntax, you can achieve the same with `Array.from(arguments)`, which is detailed on the mdn resource.

All this does is grab the dataset from your list of arguments and then iterate over every other arguments calling it, you could do the same simply by insisting your actions parameter is an array.

Link to comment
Share on other sites

Yeah, fn isn't reserved, list of reserved words here, you could also use `func` but, rather than the generic word `function` we should probably be a bit more explicit by naming this function by what it does, in this case, maybe `iterator` would be more suitable and more useful for the next developer (or future self) to be able to quickly understand what the code does.

Note that arrow functions can have an implicit return if you don't create an explicit function body, so, for example:

const map = fn => list => list.map(fn)
const square = i => i * i
const squareList = map(square)

const arr = [1, 2, 3]
squareList(arr)
// 1, 4, 9

There is so much going on here, and the reuse it fantastic.

`square` could be rewritten as:

function square (num) {
  return num * num
}

but arrows functions make this more succinct and even though we haven't explicitly used the `return` syntax we are in fact returning the square of the argument given to the function.

Similarly we do the same thing for `map` where we return the output of `list.map(fn)` and pass the function `fn` to each entry in the list.

This sort of pattern is fantastic for reuse as to create functionality that doubles numbers in a list we can simply add a doubling function and then create a new function which can map that over an array:

const double = i => i * 2
const doubleList = map(double)

doubleList(arr)
// 2, 4, 6

We could have implemented doubling and squaring with just 2 functions, but, those functions would not be minimal, each would have to implement the number operation (doubling or squaring) AND implement the mapping algorithm (in this case just calling array.map, but, you can imagine as we move away from trivial examples this could get more complex). By extracting the mapping functionality our `double` and `square` functions deal with just one job, they are then consumed by a function that handles mapping.

This has so many benefits:

* As each function is minimal and deals with a single concern it is far far easier to test these functions and test them comprehensively.

* We test just one thing, with the naive solutions we'd also be implicitly testing our map functionality, which is sub-optimal, we want to test that stuff explicitly and thoroughly.

* If we change how we iterate over a list later (for example, maybe our lists get implemented as linked-lists or as objects, where array.map is no longer viable), we ONLY change our `map` function, everything else stays the same. This is golden, but as we've already written comprehensive tests for these functions we know that our changes don't break our library/framework/application.

* We can name functions more easily as they do just one thing. Important for debugging and more important for any developers wanting to reuse our code (including our future selves when we come back to this stuff x months from now and have forgotten what we did back then!).

Link to comment
Share on other sites

since i have print Professor Frisby's, i 'm reading it and it's very interesting thanks. Still a question...it's interesting becausew with fp i must re-learn the basic;

this function return effectively false :

var debub_position=true

              is_clic_valid =(count,f) =>{
		let condition = (count) => {
			switch(count){
				case 0:
					return true
				case 1:
					return true
				case 2:
					return true
				default:
					return false
			}
		}

		if ( condition == true && f == true ){
			return true
		}else{
			return false
		}
	}

	let ex=is_clic_valid(4,debug_position)
	console.log(ex) // return false

But in this case it's true because i change this line however i think it's correct...

		(condition && f )? true : false
var debub_position=true

              is_clic_valid =(count,f) =>{
		let condition = (count) => {
			switch(count){
				case 0:
					return true
					//break
				case 1:
					return true
					//break
				case 2:
					return true
					//break
				default:
					return false
					//break
			}
		}
		( condition && f ) ? true : false
	}

	let ex=is_clic_valid(4,debug_position)
	console.log(ex) // return true

what 's my mistake ? think also that break is not necessary in this case ? no ?

Link to comment
Share on other sites

You are making the right type of mistakes! (which is good)

 

try this:

let fn = ()=>false;
console.log( fn == true && true, fn && true);

 

a function is truthy, but when compared to true with ==, it will return false.

This seems voodo magic until you read the specs[0][1]

 

ps: there is a difference between fn, and fn(), just like in your example. You'd need `condition(count)` to make it work

 

[0] http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3

[1] http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5

 

Link to comment
Share on other sites

another mistery...

function foreach(tableau,action){
	for(var i=0;i<tableau.length;i++){
		action(tableau[i]);
	}
}


stop_tw = (tw_name,f,obj) => {
	if(f){
		obj.alpha=0

		//foreach(tw_name,game.tweens.remove) > don't works !!!!

	for(var i=0;i<tw_name.length;i++){ // works but it's the same like foreach!
		game.tweens.remove(tw_name[i])
	}
		f=false
	}
}

 

Link to comment
Share on other sites

nobody ?

first case :

//first 
console.log(condition(count)) // > true
console.log(f) // > true


//function 1
if (condition(count) && f){
  return true
}else{
  return false
}
//return logically true ok!


//function 2 but strange result
(condition(count) && f) ? true : false
//return false ... WHy ?



second case :


//function 1 > works 
stop_tw = (tw_name,f,obj) => {
	if(f){
		obj.alpha=0
	for(var i=0;i<tw_name.length;i++){ // works but it's the same like for_each!
		game.tweens.remove(tw_name[i])
	}
		f=false
	}
}


//function 2 > don't works and i don't understand why because it's the same than function 1

function for_each(tableau,action){
	for(var i=0;i<tableau.length;i++){
		action(tableau[i]);
	}
}

stop_tw_for_each = (tw_name,f,obj) => {
	if(f){
		obj.alpha=0
		for_each(tw_name,game.tweens.remove) //> don't works !!!!
		f=false
	}
}

 

Link to comment
Share on other sites

sorry, don't think I'm going to be too much help:

1st case: no idea, a ternary and an if/then should be the same, you are `return (clause) ? a : b` right? or making an assignment? Because ternaries aren't really for code branching, but for assignment.

2nd case: Still sounds like a binding issue, where the function loses its scope, or it gets altered or something, is the error that somewhere inside game.tweens.remove it can't find a property it is expecting?

Link to comment
Share on other sites

hi mattstyles,

you are alright for ternary operator ;)

(condition(count) && f) ? true : false //don't work

let cond = condition(count)

(cond && f) ? true : false //works

for the 2nd case, i have found my error. i start an another post about that. on the second post maybe you could give me some advice on my beginnings of functional programming...

 

 

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...