2010-07-21 10 views
7

Estaba trabajando en algún código el día de hoy, cuando me di cuenta, "¡Oye! Este código sería más conciso y semántico si abstraía la idea de un booleano no de un función anónima y en una función prototipo ..."JavaScript funcional: cómo implementar Function.prototype.not

Considere un generador de predicado:

function equalTo(n) { 
    return function(x) { 
     return n==x; 
    }; 
} 

para que puedas usarlo de esta manera:

[1,2,3,4,1,2,3,4].filter(equalTo(2)) == [2,2] 

Ahora, mi idea es hacer una predicar te "inverser":

Function.prototype.not = function() { 
    //??? 
} 

Así que se puede decir:

[1,2,3,4,1,2,3,4].filter(equalTo(2).not) == [1,3,4,1,3,4] 

Mi primer intento de la aplicación probablemente era muy ingenua:

Function.prototype.not = function() { 
    return ! this(arguments); 
} 

Y, probablemente, por qué no lo hizo trabajo.

¿Cómo implementaría esta función y por qué?

Estoy tratando de entender bien las ideas funcionales, y conocer JavaScript lo suficiente como para saber que se puede usar para hacer esto, pero no cómo.

+0

mientras que apruebe en general de lo que está haciendo, siento que este es un enfoque más orientado a objetos que funcional. El enfoque funcional sería como no (equalTo (2)) para crear un predicado inverso –

+0

@Luke: estoy de acuerdo. Vengo de un fondo muy OO, así que supongo que esto solo representa mi transición de OO a programación funcional ... –

+0

Soy exactamente igual, así que lo entiendo completamente :) En una nota sin relación, asegúrense de ver clojure y coffeescript (de hecho, creo que te encantará coffeescript) –

Respuesta

7

Su aplicación no funcionará por varias razones:

  • necesita devolver una función, no un valor lógico.
  • Debería pasar los argumentos tal como están, no envueltos en una matriz.
  • Debe preservar el contexto (this palabra clave) que la función habría sido llamado en

que implementaría esta manera:.

Function.prototype.not = function (context) { 
    var func = this; 
    return function() { return !func.apply(context || this, arguments); }; 
} 
  • vuelvo una función anónima (function() { ... })
  • Llamo al apply para llamar a la función original en los contextos actuales con los argumentos reales.
  • (EDIT) Bonificación gratuita: Agregué un parámetro opcional context que anulará this para la devolución de llamada.
+0

Muy esclarecedor. Gracias. –

+0

+1, muy bien hecho –

+3

Y como 'no' es una función, la llamas usando 'equalTo (2) .not()'. – Guffa

2

probablemente lo haría como tal (pero quizás con algún tipo de espacios de nombre):

function not (x) { 
    return !x; 
} 

function id (x) { 
    return x; 
} 

function compose (/*funcs*/) { 
    var args = arguments.length 
    ? Array.prototype.slice.call (arguments) 
    : [id] 
    ; 
    return function() { 
    var val = args [args.length - 1].apply (null, arguments); 
    for (var i = args.length - 2; i >= 0; --i) { 
     val = args [i] (val); 
    } 
    return val; 
    }; 
} 

[1,2,3,4,1,2,3,4].filter (compose (not, equalTo (2))); 
+0

Me gusta este método. Es menos OOPish y más flexible. –

1

Usando su idea:

function equalTo(n) { 
    var fn = function(x) { 
     return n == x; 
    }; 
    fn.not = function(x) { 
     return n != x; // use this for simpler conditions 
     return !fn.apply(this, arguments); // use this for more complex logic 
    } 
    return fn; 
} 

Así que su ejemplo funcionaría:

[1,2,3,4,1,2,3,4].filter(equalTo(2).not) == [1,3,4,1,3,4] 

Editar: Se puede escribir una función auxiliar (nombre mejor que se encuentran) por lo not no necesita ser redefinido en cada ocasión:

function generateFnWithNot(fn) { 
    return function() { 
     var f = fn.apply(this, arguments); 
     f.not = function() { 
      return !f.apply(this, arguments); 
     } 
     return f; 
    }; 
} 

Así que cuando la definición de sus funciones, puede definir como normal, con la excepción de envolver una llamada generateFnWithNot:

var equalTo = generateFnWithNot(function (n) { 
    return function (x) { 
     return n == x; 
    }; 
}); 

equalTo(5) // resolves to function() { return n == 5; } 
equalTo(5).not // resolves to function() { return n != 5; } 
+0

Esta es una idea interesante, en el sentido de que 'no' tiene el alcance 'igualA'. El único obstáculo es que no se combinaría bien con otras funciones, ya que tendrías que volver a definir 'no' para cada una. –

+0

Edité mi respuesta para incluir una función auxiliar que agregará 'no' a una función genérica, por lo que ahora' no' no necesita redefinirse para cada función; solo se requiere una llamada a función. –

Cuestiones relacionadas