4525295943286785564630898738613180402752865746514908141289658206399306834966472673922031069344230677865113693205407878709601660268
[]’s
2025 Chegou
Há 2 semanas
As sombras da programação
4525295943286785564630898738613180402752865746514908141289658206399306834966472673922031069344230677865113693205407878709601660268
[]’s
λsz.s(sz)
.S
em alguns textos) significa incremento, e é descrito como λwyx.y(wyx)
. Ou seja, +3 é 3 incrementado, que é igual a 4. 2+3 é a função 2 recebendo como parâmetros + e 3, ou seja, incrementa duas vezes o número 3, +(+3), o que resulta em 5.*2x
(ou M2x
) e sua definição é λab.a(b+)0
ou λxyz.x(yz)
.[update 2014-08-25]
A definição de decremento é (caso eu não tenha copiado errado):
[/update]DEC = λnfx.n (λgh.h(gf)) (λu.x) I
f = λx.DEC(*2x)
, o ponto fixo é aquele onde fx = x
, ou seja, o resultado é igual ao parâmetro.f(x) = 2x - 1 x = 2x - 1 2x - 1 = x 2x - 1 - x = 0 x - 1 = 0 x = 1
λx.DEC(*2x)
é 1.x = fx
x
por fx
:x = fx = f(fx) = f(f(fx))) = ...
λfx.Δxf
x
e f
são vinculadas e a variável Δ
é livre.λx.xx
λx.y
fx
igual a *2x
em f4
, o resultado é 8 – fx
é o dobro e x
e o caso é f4
. Isso é descrito assim:let fx = *2x in f4
x
, sendo fa
igual a *2a
em fx-1
:λx.let fa = *2a in DEC(fx)
λx.DEC(*2x)
func x = let f a = 2 * a in f x - 1
(describe func
(λ (x)
(let ((f (λ (a) (* 2 a)))
(- (f x) 1))))
let fx = y in z
(λf.z)(λx.y)
FACT = λn.(ISZERO n) 1 (*n (FACT (DEC n)))
ISZERO
, 1
, *
e DEC
), ainda há a variável livre FACT
, que não é definida na assinatura da função.[update 2014-08-25]
A definição deISZERO
é:
[/update]TRUE = λxy.x FALSE = λxy.y ISZERO = λn.n(λx.FALSE)TRUE
ALMOST-FACT = λfn.(ISZERO n) 1 (*n (f (DEC n)))
yf = f(yf)
fix f = f (fix f)
(define fix
(λ (f)
(f (fix f))))
f
, queremos o ponto x
onde fx
seja igual a x
… é uma construção let!f
(λf.
), seja x
igual a fx
(let x = fx
) em x
(in x
):λf.let x = fx in x
fix :: (a -> a) -> a
fix f = let x = f x in x
x
:λf.let x = fx in x
λf.let xx = f(xx) in xx
λf.(λx.xx)(λx.f(xx))
λx.f(xx)
à função λx.xx
, temos (λx.f(xx))(λx.f(xx))
, exatamente o combinador Y de Curry:Y = λf.(λx.f(xx))(λx.f(xx))
(define Y
(λ (f)
((λ (x) (f (x x))) (λ (x) (f (x x))))))
λsv.sv
(que é o número 1, equivalente à identidade: I = λx.x
). Por exemplo:g = 1g
g = (λsv.sv) g
g = (λv.gv)
Y = lambda f: (lambda x: x(x))(lambda x: f(x(x)))
xx
por λv.xxv
, tudo se resolve:Y = lambda f: (lambda x: x(x))(lambda x: f(lambda *args: x(x)(*args)))
Θ = (λx.xx)(λab.b(aab))
Z = λf.(λx.xx)(λx.f(λv.xxv))
B = λwyx.w(yx)
∆ = λx.xx
N = B∆(B(B ∆)B)
[update 2014-05-25]
Faglia nostra: o programador Prolog e criador da linguagem Erlang é Joe Armstrong. Simon Thompson é o mantenedor e um dos resposáveis pela reescrita em C.
[/update]
fact(0, 1) :- !.
fact(N, F) :- N > 0,
N1 is N - 1,
fact(N1, F1),
F is N * F1.
fact(0) -> 1;
fact(N) when N > 1 -> N * fact(N - 1).
fact(N, F) :- N >= 0,
fact(N, 1, F).
fact(0, F, F) :- !.
fact(N, A, F) :- N1 is N - 1,
A1 is N * A,
fact(N1, A1, F).
fact(N) when N >= 0 -> fact(N, 1).
fact(0, A) -> A;
fact(N, A) -> fact(N-1, A*N).
fact(N, F) :- N >= 0, fact(N, 1, F).
fact(0) --> { ! }, '='.
fact(N) --> { N1 is N - 1 }, mul(N), fact(N1).
mul(N, A, R) :- R is N * A.
-*- Prolog -*-
:- module(profile, [profile/2]).
:- dynamic profile/2.
set(ID, X) :- profile(ID, X), !.
set(ID, X) :- X =.. [Attr, _],
E =.. [Attr, _],
retractall(profile(ID, E)),
assertz(profile(ID, X)), !.
set(_, []) :- !.
set(ID, [X|Rest]) :- set(ID, X), set(ID, Rest).
get(ID, R) :- findall(X, profile(ID, X), R).
drop(ID) :- retractall(profile(ID, _)).
persistency.pl
.:- use_module(library(persistency)).
profile/2
como dinâmico, em vez disso vamos declarar uma persistência. Substituia a linha com dynamic
por:
:- persistent profile(id:atom, attr:compound).
retractall/1
por retractall_profile/2
, protegidas por with_mutex/2
. retractall(profile(ID, E)),
with_mutex(profile,
retractall_profile(ID, E)),
drop(ID) :- retractall(profile(ID, _)).
drop(ID) :- with_mutex(profile,
retractall_profile(ID, _)).
assertz/1
por assert_profile/2
. Onde está:
assertz(profile(ID, X)), !.
with_mutex(profile,
assert_profile(ID, X)), !.
with_mutex/2
.connect(File) :- db_attach(File, [gc]).
connect :- connect('profile.db').
-*- Prolog -*-
:- module(profile, [profile/2]).
:- persistent profile(id:atom, attr:compound).
set(ID, X) :- profile(ID, X), !.
set(ID, X) :- X =.. [Attr, _],
E =.. [Attr, _],
with_mutex(profile,
(retractall_profile(ID, E),
assert_profile(ID, X))), !.
set(_, []) :- !.
set(ID, [X|Rest]) :- set(ID, X), set(ID, Rest).
get(ID, R) :- findall(X, profile(ID, X), R).
drop(ID) :- with_mutex(profile,
retractall_profile(ID, _)).
connect :- connect('profile.db').
connect(File) :- db_attach(File, [gc]).
'…'
). Por exemplo: hanoi
hanoi(5, R)
X = 2
'='(X, 2)
_
). Por exemplo: X
default(5).
:-
e então o predicado ao qual ele está condicionado. Por exemplo:.move(_, X, Y, _, R) :- move(1, X, Y, _, R).
:-
e terminada por ponto. No prompt ?-
basta digitar o predicado seguido de um ponto e pressionar a tecla Enter.','/2
) que avalia o primeiro um predicado (argumento) e só avalia o segundo se o primeiro for verdadeiro, ou seja, E lógico.N1 is N - 1, fact(N1, F1)
';'/2
) que avalia o primeiro predicado e só avalia o segundo se o primeiro for falso, ou seja, OU lógico.'!'/0
) que indica que, se a regra atual “casar” (match) com a pergunta, os próximos fatos ou regras não serão avaliados.% -*- Prolog -*-
:- module(hanoi, [hanoi/0, hanoi/1, hanoi/2]).
hanoi :- hanoi(R), findall(_, show(R), _).
hanoi(R) :- default(N), hanoi(N, R).
hanoi(N, R) :- N > 0,
findall(X,
move(N,
'a esquerda',
'a direita',
'o centro', X),
R).
default(5).
move(1, X, Y, _, move(X, Y)) :- !.
move(N, X, Y, Z, R) :- N1 is N - 1, move(N1, X, Z, Y, R).
move(_, X, Y, _, R) :- move(1, X, Y, _, R).
move(N, X, Y, Z, R) :- N1 is N - 1, move(N1, Z, Y, X, R).
show(move(X, Y)) :- format('mova o disco d~w para ~w~n', [X, Y]).
show([X|_]) :- show(X).
show([_|Rest]) :- show(Rest).
bash$ prolog -f hanoi.pl \
-g 'use_module(library(save)), qsave_program(hanoi, [goal(hanoi)])' \
-t halt
-->
:
a --> b, c(x), d.
a(Start, End) :- b(Start, V1), c(x, V1, V2), d(V2, End).
% -*- Prolog -*-
:- module(profile, [profile/2]).
profile
, que exporta o predicado profile/2
(com aridade 2).profile/2
:
:- dynamic profile/2.
profile/2
é dinâmico, ou seja, muda ao longo da execução do programa.set(ID, X) :- profile(ID, X), !.
set(ID, X)
– onde ID
é o identificador do perfil e X
é um atributo do perfil – e essa combinação for verdadeira, ele não deve assumir isso como ver dade e não avaliar demais regras ou fato (!
).set(ID, X) :- X =.. [Attr, _],
set(ID, X)
(exatamente igual ao anterior), e X
é um termo composto de aridade 1, cujo cabeçalho será atrelado à incógnita Attr
. E =.. [Attr, _],
E
é termo composto com mesmo cabeçalho e aridade de X
, porém com parâmetro _
, que significa qualquer coisa.X
for name(john)
, E
será name(_)
.
retractall(profile(ID, E)),
assertz(profile(ID, X)).
set(_, []) :- !.
set/2
para aquele atributo e continuar chamando para o resto da lista:
set(ID, [X|Rest]) :- set(ID, X), set(ID, Rest).
get(ID, R) :- findall(X, profile(ID, X), R).
drop(ID) :- retractall(profile(ID, _)).
?- [profile].
true.
?- profile:set(1, [name(john), age(20), city(rio)]).
true.
?- profile:set(2, [name(jack), age(21), city(sao_paulo)]).
true.
?- profile(ID, city(City)).
ID = 1
City = rio ;
ID = 2
City = sao_paulo.
?- profile:get(1, R).
R = [name(john), age(20), city(rio)].
?- profile:drop(1).
true.
?- profile(ID, _).
2 ;
2 ;
2.
?- drop(_).
true
?- profile(ID, _)
false.
?-
gtrace
.__*__
) são reservados e chamados especiais. Você não deve implementá-los se não souber o que está fazendo.__*
).Person
, todos os métodos iniciados com sublinha dobrado serão prefixados com _Person
. Assim, __fix_data
vira _Person__fix_data
._*
) são protegidos, ou seja, só devem ser acessados no contexto da classe e de suas subclasses.class
, a primeira coisa que acontece é o construtor (__new__
) da metaclasse (por padrão type
) ser chamado.str
), uma tupla contendo as classes base da classe criada e um dicionário com métodos e atributos declarados no corpo da classe.__init__
) da metaclasse é chamado, recebendo como parâmetros o retorno do construtor, o nome da classe, a tupla de classes base e o dicionário de métodos e atributos.__call__
) da metaclasse. Ele recebe como parâmetros a classe e quaisquer parâmetros passados no comando de instanciação.super
seja retornada, caso o construtor seja sobrescrito.class Metaclasse(type):
def __new__(meta, name, base, dict_):
""" Este é o construtor da metaclasse """
return type.__new__(meta, name, base, dict_)
def __init__(cls, name, base, dict_):
""" Este é o método de inicialização da metaclasse """
type.__init__(cls, name, base, dict_)
def __call__(cls, *args, **kwargs):
""" Este é o método de chamada da metaclasse """
return type.__call__(cls, *args, **kwargs)
class Classe(object):
__metaclass__ = Metaclasse
def __new__(cls, *args, **kwargs):
""" Este é o construtor da classe """
return super(Classe, cls).__new__(cls)
def __init__(self, *args, **kwargs):
"""
Este é o método de inicialização da classe.
Como ela herda de object, não há necessidade de
chamar super, mas quando houver, a forma é:
super(Classe, self).__init__(*args, **kwargs)
"""
def __call__(self, *args, **kwargs):
""" Este é o método de chamada da classe """
return None
[update 2015-04-18] Esta é a versão do código acima em Python 3:[/update]class Metaclasse(type): def __new__(meta, name: str, base: tuple, dict_: dict) -> type: """ Este é o construtor da metaclasse """ return type.__new__(meta, name, base, dict_) def __init__(cls, name: str, base: tuple, dict_: dict): """ Este é o método de inicialização da metaclasse """ type.__init__(cls, name, base, dict_) def __call__(cls, *args, **kwargs) -> object: """ Este é o método de chamada da metaclasse """ return type.__call__(cls, *args, **kwargs) class Classe(object, metaclass=Metaclasse): def __new__(cls, *args, **kwargs) -> object: """ Este é o construtor da classe """ return super().__new__(cls) def __init__(self, *args, **kwargs): """ Este é o método de inicialização da classe. Como ela herda de object, não há necessidade de chamar super, mas quando houver, a forma é: super().__init__(*args, **kwargs) """ def __call__(self, *args, **kwargs) -> None: """ Este é o método de chamada da classe """ return None
__del__
) não é chamado quando o objeto perde todas as referências ativas, mas sim quando é coletado pelo garbage collector (gc
).gc
não tem hora certa para rodar e seu comportamento varia muito de uma implementação de Python para outra, não é recomendável confiar nele para executar procedimentos críticos.[update]
Um detalhe importante é que, se o método de inicialização for sobrescrito alterando sua assinatura, não há necessidade de sobrescrever o construtor, porém se o construtor for sobrescrito alterando sua assinatura, é obrigatório que o método de inicialização também seja sobrescrito.
Detalhes da linguagem…
[/update]
return
) é:
type.__call__(cls, *args, **kwargs)
[update]
Um detalhe que esqueci de mencionar é que é justamente nessa chamada que o construtor e o método de inicialização são evocados.
[/update]
RLock
threading.RLock
.lock = RLock()
thr1 = Thread(target=func1, args=(lock, ))
thr2 = Thread(target=func2, args=(lock, ))
thr3 = Thread(target=func3, args=(lock, ))
acquire
) no inicío e liberá-lo (release
) ao final. Por exemplo:
def func1(lock):
lock.acquire()
try:
# executa o procedimento
...
finally:
lock.release()
Person
com dados, como identity_code
(CPF) que podem sofrer alterações em threads diferentes (sei que não é uma boa abordagem, mas apenas como exemplo).def lock(wrapped):
lock_ = RLock()
@wraps(wrapped)
def wrapper(*args, **kwargs):
with lock_:
return wrapped(*args, **kwargs)
return wrapper
class Person(object):
...
@property
def identity_code(self):
return self.__identity_code
@identity_code.setter
@lock
def identity_code(self, value):
self.__identity_code = value
...
class ObjectLocker(object):
def __init__(self, obj):
self.__obj = obj
self.__lock = RLock()
def __enter__(self):
self.__lock.acquire()
return self.__obj
def __exit__(self, etype, exc, traceback):
self.__lock.release()
with
para acessar o objeto original.with
, o objeto original será trancado para o thread atual e liberado ao final.locker = ObjectLocker(Person(...))
thr = Thread(target=func, args=(locker, ))
func
a instância de Person
deve ser acessa da seguinta forma:
with locker as person:
name = person.name
person.identity_code = data['identity_code']
RLock
, chamando seu método acquire
, e encerre chamando seu método release
. Tome cuidado para que seja usada a mesma instância de RLock
!threading
: Condition
, Event
, Lock
, RLock
e BoundedSemaphore
. Eles são seus amigos. ;-)threading
por dummy_threading
para os testes unitários, mas use threading
para testes de aceitação.from cPickle import dumps, loads
import gdbm
__all__ = ['Banco', 'Notas']
class Banco(object):
def __init__(self, file=None):
if file is None:
file = 'notas.db'
if isinstance(file, basestring):
file = gdbm.open(file, 'c')
self.file = file
def close(self):
if self.file:
self.file.close()
self.file = None
class Notas(object):
def __init__(self, matricula, db):
self.matricula = matricula
if not isinstance(db, Banco):
raise TypeError(
'expected Banco instance, got {}'
.format(type(db).__name__)
)
self.db = db
try:
self.notas = loads(db[str(matricula)])
except KeyError:
self.notas = {}
def save(self):
db[str(self.matricula)] = dumps(self.notas)
@property
def primeiro_bimestre(self):
return self.notas.get('1bim')
@primeiro_bimestre.setter
def primeiro_bimestre(self, value):
self.notas['1bim'] = float(value)
@property
def segundo_bimestre(self):
return self.notas.get('2bim')
@segundo_bimestre.setter
def segundo_bimestre(self, value):
self.notas['2bim'] = float(value)
@property
def terceiro_bimestre(self):
return self.notas.get('3bim')
@terceiro_bimestre.setter
def terceiro_bimestre(self, value):
self.notas['3bim'] = float(value)
@property
def quarto_bimestre(self):
return self.notas.get('4bim')
@quarto_bimestre.setter
def quarto_bimestre(self, value):
self.notas['4bim'] = float(value)
@property
def recuperacao(self):
return self.notas.get('rec')
@recuperacao.setter
def recuperacao(self, value):
return self.notas['rec'] = float(value)
@property
def media(self):
soma = sum(
self.primeiro_bimestre or 0,
self.segundo_bimestre or 0,
self.terceiro_bimestre or 0,
self.quarto_bimestre or 0,
)
m = soma / 4.
rec = self.recuperacao
if rec is not None:
m = (m + rec) / 2.
return m
class NotaSerializavelMixin(object):
def load(self):
s = self.retrieve()
self.notas = loads(s) if s else {}
def __str__(self):
return dumps(self.notas)
class PersistenciaMixin(object):
def retrieve(self):
try:
return self.db[str(self.matricula)]
except KeyError:
return None
def save(self):
db[str(self.matricula)] = str(self)
class NotasMixin(object):
@property
def primeiro_bimestre(self):
return self.notas.get('1bim')
@primeiro_bimestre.setter
def primeiro_bimestre(self, value):
self.notas['1bim'] = float(value)
...
@property
def media(self):
soma = sum(
self.primeiro_bimestre or 0,
self.segundo_bimestre or 0,
self.terceiro_bimestre or 0,
self.quarto_bimestre or 0,
)
m = soma / 4.
rec = self.recuperacao
if rec is not None:
m = (m + rec) / 2.
return m
class Notas(NotaSerializavelMixin, PersistenciaMixin, MotasMixin):
def __init__(self, matricula, db):
self.matricula = matricula
if not isinstance(db, Banco):
raise TypeError(
'expected Banco instance, got {}'
.format(type(db).__name__)
)
self.db = db
self.load()
save()
para salvá-las em arquivo, porém agora cada aspecto está isolado e encapsulado em seu próprio mixin.
Atualizado no blog novo.
@app.route('/people/<uuid>/', methods=['PATCH'])
def update_person(uuid):
person = db.person.find({ '_id': uuid }).first()
if not person:
raise Http404
try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }
person.update(data)
db.person.save(person)
r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)
return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }
update_person
(atualiza pessoa) faz muito mais do que simplesmente atualizar os dados:
def retrieve_person_aspect(view):
@wraps(view)
def wrapper(uuid):
person = db.person.find({ '_id': uuid }).first()
if not person:
raise Http404
return view(person)
return wrapper
@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
def update_person(person):
try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }
person.update(data)
db.person.save(person)
r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)
return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }
def parse_data_aspect(view):
@wraps(view)
def wrapper(person):
try:
data = json.loads(request.data)
except ValueError:
return json.dumps({ 'error': 'invalid request' }), \
400, \
{ 'Content-Type': 'application/json' }
return view(person, data)
return wrapper
def retrieve_person_aspect(view):
...
@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)
r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
s = ';'.join('{}:{}'.format(k, v) for k, v in r)
return json.dumps({ 'etag': md5(s).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }
update_person
já está muito mais limpa: atualiza o documento, serializa e retorna o hash, mas ainda faz coisas demais. Vamos separar o tratamento do retorno:
def respond_etag_aspect(view):
@wraps(view)
def wrapper(person, data):
response = view(person, data)
return json.dumps({ 'etag': md5(response).hexdigest() }), \
200, \
{ 'Content-Type': 'application/json' }
return wrapper
def parse_data_aspect(view):
...
def retrieve_person_aspect(view):
...
@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
@respond_etag_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)
r = [(str(k), repr(v)) for k, v in person.iteritems()]
r.sort()
return ';'.join('{}:{}'.format(k, v) for k, v in r)
update_person
faz agora além de atualizar o documento é serializá-lo. Isso também pode ser isolado:
def serialize_person_aspect(view):
@wraps(view)
def wrapper(person, data):
response = view(person, data)
r = [(str(k), repr(v)) for k, v in response.iteritems()]
r.sort()
return ';'.join('{}:{}'.format(k,v) for k, v in r)
def respond_etag_aspect(view):
...
def parse_data_aspect(view):
...
def retrieve_person_aspect(view):
...
@app.route('/people/<uuid>/', methods=['PATCH'])
@retrieve_person_aspect
@parse_data_aspect
@respond_etag_aspect
@serialize_person_aspect
def update_person(person, data):
person.update(data)
db.person.save(person)
return person
retrive_person_aspect
apenas recupera o documento do banco;parse_data_aspect
apenas faz o parsing dos dados recebidos;respond_etag_aspect
apenas gera o formato correto da resposta;serialize_person_aspect
apenas serializa o documento;update_person
apenas atualiza o documento.db
é um objeto de banco de dados MongoDB, apenas para fim de exemplo.app
é uma aplicação Flask, apenas para fim de exemplo.import
s foram omitidos:
import json
from functools import wraps
from hashlib import md5