Ir al contenido principal

This is my blog, more about me at marianoguerra.github.io

🦋 @marianoguerra.org 🐘 @marianoguerra@hachyderm.io 🐦 @warianoguerra

Sobre tipos y lenguajes de scripting

me llego un call for papers para este workshop http://www.cs.purdue.edu/homes/wrigstad/stop09/STOP/About.html

y se me ocurrió lo que se puede hacer con lenguajes de scripting para los "locos por los tipos"

acá va el código y después la explicación

ensure.py: provee los decoradores para asegurar tipos en parámetros y valor de retorno

import functools

ensure_types = True

def args_types(*args_types, **kwargs_types):
def wrapper(function):
@functools.wraps(function)
def replacement(*args, **kwargs):
for (count, (arg, arg_type)) in enumerate(zip(args, args_types)):
if type(arg) != arg_type:
raise ValueError('Invalid type for arg ' + str(count) +
' expected ' + str(arg_type) + ' got ' + str(type(arg)))

for name, ktype in kwargs_types.iteritems():
if name in kwargs:
if type(kwargs[name]) != ktype:
raise ValueError('Invalid type for karg "' + name +
'" expected ' + str(ktype) + ' got ' + str(type(kwargs[name])))

return function(*args, **kwargs)

if ensure_types:
replacement._is_wrapped = True
return replacement
else:
function._is_wrapped = False
return function

return wrapper

def return_type(return_type):
def wrapper(function):
@functools.wraps(function)
def replacement(*args, **kwargs):
result = function(*args, **kwargs)

if type(result) != return_type:
raise ValueError('Invalid return type expected ' +
str(return_type) + ' got ' + str(type(result)))

return result

if ensure_types:
replacement._is_wrapped = True
return replacement
else:
function._is_wrapped = False
return function

return wrapper


test_ensure.py: prueba la implementación
import ensure
# if ensure.ensure_types is set to False then the decorators
# return the same functions and don't make the type checking
# that an be done when development finished and you want to
# deliver the code (it remove the checks and make the code faster)
# ensure.ensure_types = False

@ensure.args_types(int, str, bool, float, sure=bool, price=float)

def do_something(number, name, sure=False, price=10.0):

print number, name, sure, price

@ensure.return_type(int)

def return_param(param):
return param

if do_something._is_wrapped:

print 'do_something contains type checks'
else:
print 'do_something doesn\'t contains type checks'

if return_param._is_wrapped:

print 'return_param contains type checks'
else:
print 'return_param doesn\'t contains type checks'

print do_something.__name__

print

do_something(1, "spongebob", True)
do_something(1, "spongebob", sure=False)

do_something(1, "spongebob", sure=False, price=2.0)

# Fail
try:
do_something(False, "spongebob")
except ValueError, error:

print error

try:
do_something(1, 12.3)

except ValueError, error:
print error

try:

do_something(1, "patricio", 10)
except ValueError, error:

print error

try:
do_something(1, "patricio", True, 10)

except ValueError, error:
print error

try:

do_something(1, "patricio", sure=None)
except ValueError, error:

print error

try:
do_something(1, "patricio", sure=True, price=None)

except ValueError, error:
print error

print 'returned', return_param(5)

try:
print 'returned', return_param("hi!")
except ValueError, error:

print error


la salida corriendo con ensure.ensure_types = True es:

do_something contains type checks
return_param contains type checks
do_something

1 spongebob True 10.0
1 spongebob False 10.0
1 spongebob False 2.0
Invalid type for arg 0 expected <type 'int'> got <type 'bool'>
Invalid type for arg 1 expected <type 'str'> got <type 'float'>
Invalid type for arg 2 expected <type 'bool'> got <type 'int'>
Invalid type for arg 3 expected <type 'float'> got <type 'int'>
Invalid type for karg "sure" expected <type 'bool'> got <type 'NoneType'>
Invalid type for karg "price" expected <type 'float'> got <type 'NoneType'>
returned 5
returned Invalid return type expected <type 'int'> got <type 'str'>

la salida corriendo con ensure.ensure_types = False es:

do_something doesn't contains type checks
return_param doesn't contains type checks
do_something

1 spongebob True 10.0
1 spongebob False 10.0
1 spongebob False 2.0
False spongebob False 10.0
1 12.3 False 10.0
1 patricio 10 10.0
1 patricio True 10
1 patricio None 10.0
1 patricio True None
returned 5
returned hi!

Conclusión:

con esta librería se le podría dar a los que lo quieran (no se si yo lo usaría mucho) una forma de checkear en tiempo de ejecución los tipos de datos recibidos como parámetros y los tipos devueltos por las funciones que decidan decorar.
Estas decoraciones sirven también como método para dar hints a los programadores sobre de que tipos son los valores que se reciben en la función y puede ser usado por IDEs para deducir los tipos y mostrar errores/warnings en el cuerpo del código.
Un aspecto positivo es que se puede deshabilitar el decorado seteando ensure.ensure_types = False. En tal caso no se incurre en ningún overhead en tiempo de ejecución, esto podría ser usado luego del ciclo de desarrollo y prueba, donde no se detecto ninguna "violacion de contrato" y se entrega al usuario final el código con los checks en tiempo de ejecución deshabilitados para incrementar la performance de la aplicación.

obviamente se pueden hacer mejoras en el decorador de los parámetros, pero para ser un modulo de 50 lineas y escrito en menos de una hora, esta bastante bien :)

argumentos variables en php

queres tener una funcion con argumentos variables en php?
tenes un array y queres llamar a una funcion con esos argumentos?
's/funcion/metodo/g? ;)

entonces este post es para vos!

salio marquerinero el arranque...

bueno ante esas necesidades hice este ejemplito, es un poco tricky pero se puede hacer..

aca va

<?php

function suma($num1, $num2)
{
return $num1 + $num2;
}

function resta($num1, $num2)
{
return $num1 - $num2;
}

function operacion()
{
$args = func_get_args();
$op = array_shift($args);
print($op . '=');
if($op == "suma")
{
print(call_user_func_array('suma', $args));
}
elseif($op == "resta")
{
print(call_user_func_array('resta', $args));
}
else
{
print('?');
}
}

class Foo
{
public function __construct()
{

}

public function suma($num1, $num2)
{
return $num1 + $num2;
}

public static function resta($num1, $num2)
{
return $num1 - $num2;
}
}

$foo = new Foo();

operacion("suma", 2, 2);
operacion("resta", 2, 2);
operacion("asd", 2, 2);
// como llamar a un metodo de un objeto
print('$foo->suma=' . call_user_func_array(array($foo, 'suma'), array(2, 2)));
// como llamar al metodo de una clase
print('Foo::resta=' . call_user_func_array(array('Foo', 'resta'), array(2, 2)));

?>

La API de los bancos esta rota

Tengo 3 cheques de pago diferido, lo primero que piensa un ser como yo que nunca en su vida pensó en bancos (y no le interesa pensar en ellos, para eso están, para pensar por nosotros) supongo que si yo los deposito antes ellos se van a tomar el trabajo de guardarlos en algún lugar y el día en el que se deban cobrar los cobran y los depositan en mi cuenta.

Suena normal no? para eso están los bancos, para encargarse de todas esas cosas relacionadas con el dinero que nosotros no queremos saber.

Eso supuse.. resulta que me llega una carta diciendo que no puedo depositar los cheques antes del tiempo del cobro. A mi me pareció la institución mas user-unfriendly del mundo, pero mi viejo que esta acostumbrado a los bancos me dijo "como vas a hacer eso?", lo único que hace es recibir la plata de mi sueldo, ponerla en algún lugar, pagarme un misero interés mucho inferior a la tasa de inflación, usar mi plata para dar créditos (incrementando su valor 9 veces), cobrándome cuando saco plata por el cajero y encima cuando quiero que hagan algo por mi, la primera cosa, no la pueden hacer.

Encima tuve que estar una hora en el banco preguntando haciendo cola y llenando papelitos que supongo que ellos tendrían que llenar (o no tendrían que existir) para que después me digan que mi transacción es invalida 7 días después!.

Encima le dije al cajero, fíjate si esta todo bien porque es la primera ves que deposito un cheque en un banco, miró un poco y me dijo que estaba bien. Ponganle un poco de validación en el front end así no tengo que esperar que mi request llegue al backend para que me lo rebote, eso consume mucho ancho de banda, recursos en el backend y encima me tiene a mi esperando (les suena similar a algo?). Pero desde otro punto de vista hagan una base de datos con cheques en pago diferido depositados antes de fecha y ponganle un cron que todas las mañanas se fije en la fecha de cobro y decida cuales ya se pueden cobrar.

ya casi 150 años tienen estas instituciones y su GUI/API externa/frontend sigue siendo la misma de siempre.

Ahora tengo que ir a buscar los benditos cheques, guardarlos y anotar en algún lado 3 fechas para ir a depositarlos por separado haciendo 3 horas de tramites, llenando 3 papelitos por separado para que ellos tengan la amabilidad de hacerme disponible ese dinero.

Sobre los comentarios anonimos y los malentendidos

Mi ultimo post hacia referencia a que porque la proxima release de ubuntu va a empezar con K iba a apestar, si uno piensa un poco puede ver que no tiene sentido racional y que obviamente si leemos el titulo "Flame mode = on" naturalmente es un chiste o algo sin sentido.

Quien asociaría la calidad de una distribución linux en base a la primera letra de la misma?

Parece que alguien si, ya que comento "Guau. Una pelotudez importante." si es una pelotudez, mi blog esta lleno de chistes sin sentido, particularmente ese post, si uno lo seleccionaba podía leer un texto que estaba en blanco que decía PD: todo bien con KDE, es mas un flame a tuza que otra cosa y si incluso no se hubiese leído, era o obra de un troll o una mera asociación tonta de letras. Al no considerarme un troll es obvio que fue meramente un chiste, pero hay gente dispuesta a ser ofendida e interpretar mal cualquier cosa en internet. No se si debería haber posteado esto, ya que me estoy dejando llevar por ellos, pero es solo para aclarar porque ayer me agregaron al planeta de pyar y por ahí no saben que posteo semejantes giladas.

les dejo un chiste alusivo

bienvenidos los de pyar y por favor no se ofendan si algo no me gusta lo fundamento, si sale a modo troll probablemente sea un chiste :)

project euler - problema 5

Hasta ahora a todos los problemas los venia resolviendo por fueza bruta aplicando algunas optimizaciones (para que no sea tan bruto vio?), pero decidi ejercitar un poco mis conocimientos matematicos para intentar resolverlo puramente con matematicas o al menos eliminar muchas cosas innecesarias.

el problema 5 es este:

2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.

What is the smallest number that is evenly divisible by all of the numbers from 1 to 20?

agarre un lapiz y un papel (posta!) y me puse a pensar un poco.

Lo primero que probe (fuerza bruta matematica) es el producto de los numeros del 1 al 20 es divisible por todos ellos, el problema es que no es el mas chico.. entonces pense un poco mas. Porque no es el mas chico? bueno, porque ahi hay muchas multiplicaciones innecesarias, si es multiplo de 20 es tambien multiplo de 10, 5, 4 2 etc.

despues de eso llegue a la conclusion de que el numero era el producto de los numeros del 11 al 20, pero resulto no ser asi, pense un rato mas y no se me ocurrio nada asi que decidi darle mi problema acotado a python, lo que hice a grandes rasgos fue.

incrementar de a 380 el contador (producto de 20 y 19), y ya que incremento en multiplos de 20 y 19 no me hace falta controlar que sean multiplos de ellos, por lo tanto hice una lista de numeros del 18 al 11 ya que los numeros mas chicos se comprueban comprobando esos. Puse los numeros al reves ya que si el numero divisor es mas grande tiene menos numeros multiplos, por lo tanto al cortar al encontrar un numero no divisor en los numeros mas grandes me ahorro algunos calculos innecesarios.

cuando entre a las soluciones vi que la solucion del problema tenia que ver con multiplicar numeros, pero no los numeros en si, sino las potencias mas altas de los primos de la factorizacion del 1 al 20. (aca esta la explicacion http://mathforum.org/library/drmath/view/62527.html)

y bue, tan bueno en la matematica no soy :D

codigo en python

NUMS = [float(x) for x in range(11, 19)]
NUMS.reverse()

def first_multiple_from_1_to_20():
num = 380

while True:
for x in NUMS:
if num % x != 0:
break
else:
return num

num += 380

num = first_multiple_from_1_to_20()

print num


erlang

-module(ej_005).
-export([first_multiple_from_1_to_20/0]).

is_multiple_from_11_to_18(_Value, 10) -> true;
is_multiple_from_11_to_18(Value, Number) ->
case Value rem Number == 0 of
true -> is_multiple_from_11_to_18(Value, Number - 1);
false -> false
end.

is_multiple_from_11_to_18(Value) -> is_multiple_from_11_to_18(Value, 18).

first_multiple_from_1_to_20(Count) ->
case is_multiple_from_11_to_18(Count) of
true -> Count;
false -> first_multiple_from_1_to_20(Count + 380)
end.

first_multiple_from_1_to_20() -> first_multiple_from_1_to_20(380).



lisp

(defun is-multiple-from-11-to-18 (value)
(= (loop for i from 18 downto 11 by 1
while (= (mod value i) 0) finally (return i)) 10))

(defun first-multiple-from-1-to-20 ()
(loop for i from 380 by 380
while (not (is-multiple-from-11-to-18 i))
finally (return i)))

(print (first-multiple-from-1-to-20))

soy medio idiota

no hace falta ni resaltarle la sintaxis


is_palindrome(Number) ->
String = integer_to_list(Number),
String == lists:reverse(String).

(defun is-palindrome (num)
(setf str (format nil "~a" num))
(equal str (reverse str)))

def is_palindromic(number):
'''return True if the number is a palindrome'''
number = str(number)
return number == number[::-1]


ganas de complicarse...

project euler - problema 4

Ahora que me hice un tiempo encare el cuarto ejercicio, facil, pero me costo sacarme de la cabeza tratar de hacer todo en lisp con recursion y usar el macro loop (que por cierto es el mas poderozo que he visto en cualquier lenguaje).

algunas cosas para observar:

no se si soy yo, pero usar progn y setq en lisp no me convence, siempre pense al principio en hacer todo recursivo y con let, pero parece que se complica a veces (no conozco una forma de evitar los lets nesteados, debe haber una forma pero no la conozco).

en cuanto a erlang no me gusta la diferencia de and andalso y or y orelse, pero parece que esta ahi por razones historicas, el pattern matching es adictivo, no me gusta que no pueda poner ; en el ultimo guard de un case.

hasta ahora vengo resolviendo el problema primero en python, despues en erlang y despues en lisp, por ahi eso condiciona la forma en la que lo resuelvo, pero siempre trato de usar la solucion que mas se adapta al lenguaje.

aca va el codigo

python

def is_palindromic(number):
'''return True if the number is a palindrome'''
number = str(number)

limit_start = len(number) / 2

if len(number) % 2 == 1:
limit_end = limit_start + 1
else:
limit_end = limit_start

return number[limit_start:] == number[:limit_end][::-1]

def get_largest_palindrome():
'''get the largest 3 digit palindrome'''
temp = 0

for i in range(999, 0, -1):
for j in range(i, 0, -1):
value = i * j
if is_palindromic(value):
if temp < value:
temp = value

return temp

print get_largest_palindrome()



lisp

(defun is-palindrome (num)
(progn
(setf str (format nil "~a" num))
(setf size (length str))
(setf first-half (truncate (/ size 2)))
(if (evenp size)
(setf second-half first-half)
; else
(setf second-half (+ first-half 1)))
(equal (subseq str 0 first-half) (reverse (subseq str second-half size)))))

(defun get-largest-palindrome ()
(progn
(setf temp 0)
(loop for i from 999 downto 0 do
(loop for j from i downto 0 do
(progn
(setq value (* i j))
(if (and (is-palindrome value) (> value temp))
(setq temp value)))))
temp))

(print (get-largest-palindrome))



erlang

-module(ej_004).
-compile(export_all).
%-export([get_largest_palindrome/0]).

is_palindrome(Number) ->
String = integer_to_list(Number),
Size = string:len(String),
FirstHalf = Size div 2,

case Size rem 2 == 1 of
false -> SecondHalf = FirstHalf + 1;
true -> SecondHalf = FirstHalf + 2
end,

FirstPart = string:substr(String, 1, FirstHalf),
SecondPart = lists:reverse(string:substr(String, SecondHalf, Size)),

FirstPart == SecondPart.

get_largest_palindrome(0, 0, Accum) -> Accum;
get_largest_palindrome(X, 0, Accum) -> get_largest_palindrome(X - 1, X - 1, Accum);

get_largest_palindrome(X, Y, Accum) ->
Value = X * Y,

case is_palindrome(Value) andalso Value > Accum of
true -> NewValue = Value;
false -> NewValue = Accum
end,

get_largest_palindrome(X, Y - 1, NewValue).

get_largest_palindrome(X, Accum) -> get_largest_palindrome(X, X, Accum).
get_largest_palindrome() -> get_largest_palindrome(999, 0).

project euler - problema 3

problema 3 y observaciones

el operador de modulo en erlang es muy poco intuitivo.

ej

1> 2.0 rem 2.
** exception error: bad argument in an arithmetic expression
in operator rem/2
called as 2.0 rem 2

si salta una excepcion en un guard en erlang no te dice que fue malo solo falla el guard

y alguna cosa mas que no me acuerdo.

python

import math

def factors(value):
factors = []
factor = 2.0
new_value = value
val_sqrt = math.sqrt(value)

while factor < val_sqrt:
if new_value % factor == 0:
factors.append(factor)
#new_value /= factor

factor += 1

return factors

def calculate(value):
return factors(value)[-1]

def print_results():
print calculate(600851475143)

if __name__ == '__main__':
print_results()


erlang

-module(ej_003).
-compile(export_all).

factors(Factor, _Value, Limit, Accum) when Factor > Limit -> lists:reverse(Accum);
factors(Factor, Value, Limit, Accum) when Value rem Factor == 0 ->
factors(Factor + 1, trunc(Value/Factor), Limit, [Factor | Accum]);
factors(Factor, Value, Limit, Accum) ->
factors(Factor + 1, Value, Limit, Accum).

factors(Value) -> factors(2, Value, trunc(math:sqrt(Value)), []).

print_results() ->
Factors = factors(600851475143),
io:format("~w~n", [Factors]),
if
length(Factors) > 0 -> io:format("~w~n", ([lists:last(Factors)]));
true -> io:format("No factor~n")
end.



lisp

(defun factors-helper (value limit)
(loop for factor from 2 when (= (rem value factor) 0) collect factor while (< factor limit)))

(defun factors (value) (factors-helper value (truncate (sqrt value))))

(defun print-results () (print (last (factors 600851475143))))

(print-results)