Imaginez un langage de programmation fonctionnel dont les seuls types de données sont des scalaires numériques et des imbrications arbitraires de tableaux. La langue ne dispose d'aucun moyen d'itération illimitée, les éléments suivants sont donc interdits:
- boucles explicites (pas très utiles sans effets secondaires de toute façon)
- récursivité
- fonctions arbitraires de première classe (pas de combinateur y)
La langue a cependant:
- fonctions de niveau supérieur
- reliures let à portée lexicale
- flux de contrôle de branchement
- fonctions mathématiques et logiques scalaires communes
- un constructeur de tableau simple comme fill (n, x) qui crée un tableau à n éléments avec des valeurs identiques x
- le plus important: un ensemble restreint d'opérateurs d'ordre supérieur qui effectuent une itération structurée parallèle (comme la carte, la réduction, le balayage, toutes les paires).
Pour être plus précis sur les opérateurs parallèles de données:
- y = carte (f, x) => y [i] = f [i]
- y = réduire (f, a, x) => y = f (a, f (y [p [0]], f (y [p [1]], ...))) pour une certaine permutation p
- y = scan (f, a, x) => y [i] = réduire (f, a, y [0 ... i-1])
- y = toutes les paires (f, x, y) => y [i, j] = f (x [i], y [j])
Nous pourrions également avoir d'autres opérateurs, mais pour les qualifier, ils devraient avoir un temps d'exécution polynomial, être implémentables sous un modèle raisonnable de calcul parallèle de données et utiliser au maximum l'espace polynomial.
Il y a évidemment des constructions qui ne peuvent pas être exprimées dans ce langage, telles que:
while f(x) > tol:
x <- update(x)
Que pouvons-nous exprimer dans ce système? Sommes-nous limités uniquement aux problèmes de recherche dans FP? Pouvons-nous capturer tous les algorithmes de temps polynomiaux? Existe-t-il également un ensemble minimal d'opérateurs pour cette classe?
la source
Mon autre réponse a souligné des défauts dans la langue actuelle. Dans cette réponse, je donnerai plus de détails sur les problèmes de coexistence des plis et des dépliages dans une langue totale. Ensuite, je proposerai une résolution et montrerai que tous les problèmes en P (et bien d'autres encore) peuvent être résolus dans ce langage étendu.
Plier dans une langue totale consomme des listes:
Le déploiement dans une langue totale génère des flux potentiellement illimités:
Malheureusement, les listes et les flux vivent dans des mondes différents, de sorte que ces opérateurs ne peuvent pas être composés. Nous avons besoin d'une correspondance partielle:
L'opérateur de flux intègre une liste dans un flux borné. La fonction de liste extrait les n premiers éléments (ou moins si le flux se termine plus tôt) dans une liste. On a donc l'équation suivante:
En tant qu'optimisation de l'efficacité, nous pouvons entièrement supprimer les flux en tant que structure de données intermédiaire:
Je vais maintenant esquisser une preuve que cela (avec les autres opérateurs déjà impliqués dans la question d'origine) nous permet de simuler n'importe quel algorithme polynomial.
Par définition, un langage L est dans P quand il y a une machine de Turing M et un polynôme p de telle sorte que l'appartenance de x à L peut être décidée en exécutant M au plus p (n) itérations où n = | x |. Par un argument standard, l'état de la bande de la machine dans l'itération k peut être codé avec une liste de longueur au plus 2k + 1, même si la bande de la machine est infinie.
L'idée est maintenant de représenter la transition de M en fonction de listes en listes. L'exécution de la machine se fera en dépliant l'état initial avec la fonction de transition. Cela génère un flux de listes. L'hypothèse que L est dans P signifie que nous n'avons pas besoin de regarder plus loin que p (n) éléments dans le flux. Ainsi nous pouvons composer le déroulement avec
list p(n)
pour obtenir une liste finie. Enfin, nous le replions pour vérifier si la réponse au problème de décision était oui ou non.Plus généralement, cela montre que chaque fois que nous avons un algorithme dont le temps de terminaison peut être limité par une fonction calculable dans le langage, nous pouvons le simuler. Cela suggère également pourquoi quelque chose comme la fonction d'Ackermann ne peut pas être simulé: c'est sa propre limite, donc il y a un problème de poulet et d'oeuf.
la source
C'est une langue très guipée. Essayez de programmer la fonction factorielle:
Le problème est que votre langue n'a que des plis mais ne se déplie pas. La manière naturelle d'exprimer la factorielle est de composer un dépliement de n dans la liste [1, 2, ..., n] avec le repliement qui le déchire en le multipliant.
Êtes-vous vraiment intéressé par ce langage spécifique ou par la programmation fonctionnelle totale en général? Il est évident que votre langue peut tout au plus exprimer des algorithmes en temps polynomial. Le système F (calcul lambda polymorphe) peut exprimer facilement des monstres comme la fonction d'Ackermann. Même si vous vous intéressez aux algorithmes à temps polynomial, vous avez souvent besoin de l'espace du coude super-polynomial pour les exprimer naturellement.
Edit: Comme le souligne Dave, NESL est l'un des langages de programmation fonctionnels parallèles aux données, mais il est très loin d'être total (il n'essaie même pas). La famille APL avait une trajectoire d'évolution parallèle et possède un sous-ensemble algébrique total (éviter les opérateurs à virgule fixe). Si l'objectif de votre question est la totalité, David Turner a écrit de bons articles dans ce domaine, mais pas spécifiquement sur la programmation parallèle aux données.
la source