Imagine uma linguagem de programação funcional cujos únicos tipos de dados são escalares numéricos e aninhamentos arbitrários de matrizes. O idioma não possui meios de iteração ilimitada, portanto, o seguinte não é permitido:
- loops explícitos (pouco uso sem efeitos colaterais)
- recursão
- funções arbitrárias de primeira classe (sem combinador y)
A linguagem, no entanto, possui:
- funções de nível superior
- lexicamente, deixe ligações
- fluxo de controle de ramificação
- funções matemáticas e lógicas escalares comuns
- algum construtor de matriz simples como fill (n, x), que cria uma matriz de n elementos com valores idênticos x
- o mais importante: um conjunto restrito de operadores de ordem superior que executam iterações estruturadas paralelas (como mapear, reduzir, varrer, todos os pares).
Para ser mais específico sobre os operadores paralelos de dados:
- y = mapa (f, x) => y [i] = f [i]
- y = reduzir (f, a, x) => y = f (a, f (y [p [0]], f (y [p [1]], ...))) para alguma permutação p
- y = varredura (f, a, x) => y [i] = redução (f, a, y [0 ... i-1])
- y = allpairs (f, x, y) => y [i, j] = f (x [i], y [j])
Também poderíamos ter outros operadores, mas para qualificá-los, eles deveriam ter um tempo de execução polinomial, ser implementável sob algum modelo razoável de computação paralela de dados e usar no máximo espaço polinomial.
Obviamente, existem algumas construções que não podem ser expressas nessa linguagem, como:
while f(x) > tol:
x <- update(x)
O que podemos expressar neste sistema? Estamos limitados apenas a procurar problemas no FP? Podemos capturar todos os algoritmos de tempo polinomial? Além disso, existe algum conjunto mínimo de operadores para essa classe?
fonte
Minha outra resposta apontou falhas no idioma como está. Nesta resposta, darei mais detalhes sobre os problemas com a coexistência de dobras e desdobramentos em um idioma total. Então, proponho uma resolução e mostrarei que todos os problemas em P (e de fato muitos mais) podem ser resolvidos nessa linguagem estendida.
Dobrar em um idioma total consome listas:
O desdobramento em um idioma total gera fluxos, que são potencialmente ilimitados:
Infelizmente, listas e fluxos vivem em mundos diferentes, portanto, esses operadores não podem ser compostos. Precisamos de uma correspondência parcial:
O operador de fluxo incorpora uma lista em um fluxo limitado. A função list extrai os primeiros n elementos (ou menos se o fluxo terminar anteriormente) em uma lista. Portanto, temos a seguinte equação:
Como uma otimização de eficiência, podemos cortar completamente os fluxos como uma estrutura de dados intermediária:
Agora esboçarei uma prova de que isso (com os outros operadores já implícitos na pergunta original) nos permite simular qualquer algoritmo de tempo polinomial.
Por definição, uma linguagem L está em P quando há uma máquina de Turing M e um polinômio p, de modo que a associação de x em L possa ser decidida executando M no máximo p (n) iterações em que n = | x |. Por um argumento padrão, o estado da fita da máquina na iteração k pode ser codificado com uma lista de comprimento no máximo 2k + 1, mesmo que a fita da máquina seja infinita.
A idéia agora é representar a transição de M como uma função de listas para listas. A execução da máquina será feita desdobrando o estado inicial com a função de transição. Isso gera um fluxo de listas. A suposição de que L está em P significa que não precisamos procurar mais do que p (n) elementos no fluxo. Assim, podemos compor o desdobramento
list p(n)
para obter uma lista finita. Finalmente, dobramos para verificar se a resposta para o problema de decisão foi sim ou não.De maneira mais geral, isso mostra que sempre que temos um algoritmo cujo tempo de término pode ser limitado por uma função computável na linguagem, podemos simulá-lo. Isso também sugere por que algo como a função de Ackermann não pode ser simulado: é o seu próprio limite, então há um problema de galinha e ovo.
fonte
É uma linguagem muito limitada. Tente programar a função fatorial:
O problema é que seu idioma possui apenas dobras, mas não se desdobra. A maneira natural de expressar fatorial é compor um desdobramento de n na lista [1, 2, ..., n] com a dobra que o rasga enquanto se multiplica.
Você está realmente interessado nessa linguagem específica ou na programação funcional total em geral? É óbvio que sua linguagem pode, no máximo, expressar algoritmos de tempo polinomial. O sistema F (cálculo lambda polimórfico) pode expressar monstros como a função de Ackermann com facilidade. Mesmo que seu interesse seja por algoritmos de tempo polinomial, você frequentemente precisa da sala de cotovelo super-polinomial para expressá-los naturalmente.
Edit: Como Dave ressalta, o NESL é uma das linguagens de programação paralela de dados funcionais seminais, mas está muito longe do total (nem sequer tenta). A família APL tinha uma faixa paralela de evolução e possui um subconjunto algébrico total (evite os operadores de ponto fixo). Se o foco da sua pergunta é a totalidade, David Turner escreveu alguns bons artigos nessa área, embora não especificamente na programação paralela a dados.
fonte