Para ello se requiere un adecuado análisis de dicho pro- blema, siendo necesario definir primeramente el proble- ma, para lo cual deberá de contener una detallada pero concisa descripción del mismo, un problema bien defini- do es aquel que lleva implícitas tanto una situación inicial como final claras
¿Por qué descomponer un problema en partes? Experi- mentalmente está comprobado que:
• Un problema complejo cuesta más de resolver que
otro más sencillo (de Perogrullo).
• La complejidad de un problema global es mayor que
el valor de las complejidades de cada una de sus par- tes por separado.
Según esto, merece la pena el esfuerzo de dividir un pro- blema grande en subproblemas más pequeños. Si el obje- tivo es elaborar un programa para resolver dicho proble- ma grande, cada subproblema (menos complejo) podrá ser resuelto por un módulo (subalgoritmo) relativamen- te fácil de implementar (más que el programa global No dividido). Ahora la cuestión es ¿cómo realizar la descom-
posición?; realizando un estudio descendenteTop-Down
que nos lleve desde la concepción del problema (progra- ma o algoritmo) global hasta identificar sus partes (módu- los). Esta técnica se repite aplicando una estrategia llama- dade refinamiento sucesivopropuesta por el experto en
Ciencias de la Computación Niklaus Wirth, que consis- te precisamente en volver a aplicar el estudio descendente Top-Down a cada subproblema una y otra vez hasta obte- ner subproblemas suficientemente pequeños, que puedan ser resueltos por módulos que cumplan, en la medida de lo posible, lascaracterísticas deseables en un móduloen el ámbito de la programación. En palabras del propio Ni-
klaus Wirth:
• En cada paso (del refinamiento), una o varias ins-
trucciones del programa dado, se descomponen en instrucciones más detalladas. Esta descomposición sucesiva o refinamiento de especificaciones termina cuanto todas las instrucciones están expresadas en términos de la computadora usada o del lenguaje de programación...
• Conforme se refinan las tareas, también los datos
pueden ser refinados, descompuestos o estructura- dos, siendo lo natural refinar las especificaciones del programa y de los datos en paralelo.
• Cada paso de refinamiento implica algunas decisio-
nes de diseño. Es importante que el programador sea consciente de los criterios subyacentes (en las de- cisiones de diseño adoptadas) y de la existencia de soluciones alternativas...
Problema del refinamiento sucesivo
¿Cuándo parar el refinamiento?. Un refinamiento excesi- vo podría dar lugar a un número tan grande de módulos que haría poco práctica la descomposición. Se tendrán en cuenta estos criterios para dejar de descomponer:
• Cuando no haya tareas bien definidas.
• Cuando lainterfazde un módulo sea tan complicada como el propio módulo
62.1.2 Jerarquía de módulos
Ésta es una consecuencia directa de la descomposición del problema mediante refinamientos sucesivos, el re- sultado será un conjunto de módulos estratificados en ca- pas a modo de pirámide donde en la cima habrá un único 119
módulo que representará al programa global y en los ni- veles inferiores aparecerán los módulos resultantes de las sucesivas divisiones.
Al final, debe obtenerse una estructura piramidal donde los módulos de los niveles superiores se encargan de las tareas de coordinación, lógica de la aplicación y mani- pulación de los módulos inferiores; estos otros deberán realizar tareas de cálculo, tratamiento y entrada/salida de información.
62.1.3
Independencia
Ver 'Independencia enCaracterísticas de un módulo.
62.2 Evaluando el diseño
Para evaluar o determinar cómo es de bueno un diseño estructurado se utilizan los conceptos de acoplamiento y cohesión; éstos están muy relacionados entre sí, tanto que difícilmente se puede variar uno sin que eso afecte al otro. También cabe decir que estos conceptos no son medidas que se puedan cuantificar numéricamente, son más bien magnitudes cualitativas. También se tienen en consideración los conceptos de fan-in y fan-out.
62.2.1
Acoplamiento
Se define como el grado de interdependencia que hay
entre los distintos módulos de un programa; lo desea-
ble es que esta interdependencia sea lo menor posible, es decir, un bajo acoplamiento. Los niveles de acoplamien- to, ordenados de menor (más deseable) a mayor (menos deseable) son:
• Acoplamiento normal.- Un módulo llama a otro de
un nivel inferior y tan solo intercambian datos (pa- rámetros de entrada/salida). Dentro de este tipo de acoplamiento podemos encontrarnos 3 subtipos, de- pendiendo de los datos que intercambien los módu- los:
• Acoplamiento de datos: Los módulos se comu-
nican mediante parámetros.
• Acoplamiento de marca o por estampado: Los
módulos se pasan datos con estructura de re- gistro. No es muy deseable si el módulo recep- tor sólo requiere parte de los datos que se le pasan.
• Acoplamiento de control: Los datos que se in-
tercambian entre los módulos son controles. Debido a que en este subtipo un módulo con- trola la ejecución del otro, no es un buen aco- plamiento, ya que impide que sean totalmente independientes.
• Acoplamiento Común.- Dos módulos acceden a un
mismo recurso común, típicamente memoria com- partida, una variable global o un fichero. Una varian- te de este tipo de acoplamiento es el acoplamiento externo:
• Acoplamiento externo.- Los módulos están
ligados a componentes externos. Por ejemplo, dispositivos de E/S, protocolos de comunica- ciones... etc.
• Acoplamiento de contenido.- Ocurre cuando un
módulo necesita acceder a una parte de otro módulo.
62.2.2 Cohesión
Se define como la medida de fuerza o relación fun-
cional existente entre las sentencias o grupos de sen- tencias de un mismo módulo. Un módulo cohesionado
ejecutará una única tarea sencilla interactuando muy poco o nada con el resto de módulos del programa. Se persigue que los módulos tengan una alta cohesión.
En el diseño estructurado podemos encontrarnos con los siguientes 7 tipos de cohesión (de la mejor o más deseable a la menos recomendable):
• Cohesión funcional: Los elementos del módulo es-
tán relacionados en el desarrollo de una única fun- ción.
• Cohesión secuencial: Un módulo realiza distintas
tareas en secuencia, de forma que las entradas de cada tarea son las salidas de la tarea anterior. No es una mala cohesión si las tareas implicadas no son muy complejas y requieren pocas líneas de código.
• Cohesión comunicacional: El módulo realiza ac-
tividades paralelas usando los mismos datos de en- trada y salida. Como en el caso anterior, tampoco se trata de un mal tipo de cohesión si las tareas son relativamente sencillas.
• Cohesión procedimental: El módulo tiene una se-
rie de funciones relacionadas por un procedimiento efectuado por el código (a modo debiblioteca). Es similar a la secuencial, pero puede incluir el paso de controles. Será deseable que las funciones estén rela- cionadas o realicen tareas dentro del mismo ámbito (p.e. la bibliotecastring.hdeCcontienen funciones para operar con cadenas de caracteres).
• Cohesión temporal: Los elementos del módulo es-
tán implicados en actividades relacionadas con el tiempo.
• Cohesión lógica: Las actividades que realiza el mó-
dulo tienen la misma categoría. Esto es, es como si se tuvieran partes independientes dentro del mismo módulo.
62.3. VÉASE TAMBIÉN 121
• Cohesión casual o coincidente: Los elementos del
módulo contribuyen a las actividades relacionándose mutuamente de una manera poco significativa. Este tipo de cohesión viola el principio de independencia y de caja negra de los módulos.
62.2.3
Fan-In y Fan-Out
Además de los dos conceptos anteriores, se deben tener en cuenta el grado de absorción (fan-in) y la diseminación
del control (fan-out) de los módulos para garantizar la
calidad del diseño.
• Fan-In: También llamado grado de absorción. Es
el número de superordinados inmediatos que tiene el módulo en cuestión. Es conveniente maximizar el fan-in durante el proceso de diseño, ya que cada ins- tancia de fan-in múltiple indica que se ha evitado la
duplicación de código.
• Fan-Out: También llamado diseminación del con- trol. Es el número de subordinados inmediatos que
tiene el módulo en cuestión. Conviene no tener un fan-out ni muy alto ni muy bajo, ya que eso es un posible indicador de un diseño pobre. Si no es posi- ble evitarlo, es preferible un fan-out bajo antes que uno alto.
62.3 Véase también
• Diseño orientado a objetos • Programación estructurada • Programación modular • Dinámica de sistemas • Sistema complejo • Sistema dinámico
• Encapsulamiento (programación orientada a obje- tos)
Distancia de Damerau-Levenshtein
En lateoría de la informacióny en laciencia de compu- tadores, se llama distancia de Damerau-Levenshtein o
distancia de edición al número mínimo de operaciones
requeridas para transformar una cadena de caracteres en otra. Se entiende por operación, bien una inserción, eli- minación, sustitución o transposición de dos caracteres. Lo que la distingue de ladistancia de Levenshteines que esta última cuenta como una sola operación de edición a cualquiera de las tres primeras, pero cuenta la transposi- ción como dos operaciones de edición.
63.1 Véase también
• Ispell
63.2 Enlaces externos
• Implementación en C++ de la distancia de Damerau-Levenshtein como UDF para MySQL
63.3 Referencias
Capítulo 64
Distancia de Levenshtein
La distancia de Levenshtein, distancia de edición o
distancia entre palabras es el número mínimo de ope-
raciones requeridas para transformar unacadena de ca- racteresen otra, se usa ampliamente enteoría de la in- formaciónyciencias de la computación. Se entiende por operación, bien una inserción, eliminación o la sustitu- ción de un carácter. Esta distancia recibe ese nombre en honor al científico rusoVladimir Levenshtein, quien se ocupó de esta distancia en1965. Es útil en programas que determinan cuán similares son dos cadenas de caracteres, como es el caso de loscorrectores de ortografía. Por ejemplo, la distancia de Levenshtein entre “casa”y “calle”es de 3 porque se necesitan al menos tres ediciones
elementales para cambiar uno en el otro. 1. casa → cala (sustitución de 's' por 'l') 2. cala → calla (inserción de 'l' entre 'l' y 'a') 3. calla → calle (sustitución de 'a' por 'e')
Se le considera una generalización de la distancia de Hamming, que se usa para cadenas de la misma longitud y que solo considera como operación la sustitución. Hay otras generalizaciones de la distancia de Levenshtein, co- mo ladistancia de Damerau-Levenshtein, que consideran el intercambio de dos caracteres como una operación. Como buena“distancia”,cumple (aunque es complicado demostrarlo formalmente), que:
Dist(A,B) == Dist(B,A)
Dist(A,B) + Dist(B,C) >= Dist(A,C)
64.1 El algoritmo
Se trata de unalgoritmo de tipo bottom-up, común en
programación dinámica. Se apoya en el uso de una ma- triz (n + 1) × (m + 1), donde n y m son las longitudes de las cadenas. Aquí se indica el algoritmo enpseudocódigo
para una función LevenshteinDistance que toma dos ca- denas, str1 de longitud lenStr1, y str2 de longitud lenStr2, y calcula la distancia Levenshtein entre ellos:
int LevenshteinDistance(char str1[1..lenStr1], char
str2[1..lenStr2]) // d is a table with lenStr1+1 rows and
lenStr2+1 columns declare int d[0..lenStr1, 0..lenStr2] // i and j are used to iterate over str1 and str2 declare int
i, j, cost for i from 0 to lenStr1 d[i, 0] := i for j from 0
to lenStr2 d[0, j] := j for i from 1 to lenStr1 for j from
1 to lenStr2 if str1[i] = str2[j] then cost := 0 else cost := 1 d[i, j] := minimum( d[i-1, j] + 1, // deletion d[i, j-1] + 1, // insertion d[i-1, j-1] + cost // substitution ) return d[lenStr1, lenStr2]
Elinvariantemantenido a través del algorítmo es que pue- da transformar el segmento inicial str1[1..i] en str2[1..j] empleando un mínimo de d[i,j] operaciones. Al final, el elemento ubicado en la parte INFERIOR derecha de la matriz contiene la respuesta.
64.2 Implementación
A continuación se puede ver la implementación de la fun- ción para varios lenguajes de programación. Otros len- guajes de más alto nível, como php o la funciones de usuario deMySQL, las incorporan ya, sin necesidad de implementarla para ser usada.
64.2.1 C
int Levenshtein(char *s1,char *s2) { int t1,t2,i,j,*m,costo,res,ancho; // Calcula tamanios strings t1=strlen(s1); t2=strlen(s2); // Verifica que exista algo que comparar if (t1==0) re- turn(t2); if (t2==0) return(t1); ancho=t1+1; // Re- serva matriz con malloc m[i,j] = m[j*ancho+i] !! m=(int*)malloc(sizeof(int)*(t1+1)*(t2+1)); if (m==NULL) return(−1); // ERROR!! // Rellena pri- mera fila y primera columna for (i=0;i<=t1;i++) m[i]=i; for (j=0;j<=t2;j++) m[j*ancho]=j; // Recorremos resto de la matriz llenando pesos for (i=1;i<=t1;i++) for (j=1;j<=t2;j++) { if (s1[i-1]==s2[j-1]) costo=0; else costo=1; m[j*ancho+i]=Minimo(Minimo(m[j*ancho+i- 1]+1, // Eliminacion m[(j-1)*ancho+i]+1), // Insercion m[(j-1)*ancho+i-1]+costo); } // Sustitucion // Devol- vemos esquina final de la matriz res=m[t2*ancho+t1]; free(m); return(res); }
64.2.2
C++
#include <string> #include <vector> #include <algo- rithm> using namespace std; int levenshtein(const string &s1, const string &s2) { int N1 = s1.size(); int N2 = s2.size(); int i, j; vector<int> T(N2+1); for ( i = 0; i <= N2; i++ ) T[i] = i; for ( i = 0; i < N1; i++ ) { T[0] = i+1; int corner = i; for ( j = 0; j < N2; j++ ) { int upper = T[j+1]; if ( s1[i] == s2[j] ) T[j+1] = corner; else T[j+1] = min(T[j], min(upper, corner)) + 1; corner = upper; } } return T[N2]; }
64.2.3
C#
public int LevenshteinDistance(string s, string t, out double porcentaje) { porcentaje = 0; // d es una tabla con m+1 renglones y n+1 columnas int costo = 0; int m = s.Length; int n = t.Length; int[,] d = new int[m + 1, n + 1]; // Verifica que exista algo que comparar if (n == 0) return m; if (m == 0) return n; // Llena la primera columna y la primera fila. for (int i = 0; i <= m; d[i, 0] = i++) ; for (int j = 0; j <= n; d[0, j] = j++) ; /// recorre la matriz llenando cada unos de los pesos. /// i columnas, j renglones for (int i = 1; i <= m; i++) { // recorre para j for (int j = 1; j <= n; j++) { /// si son iguales en posiciones equidistantes el peso es 0 /// de lo contrario el peso suma a uno. costo = (s[i - 1] == t[j - 1]) ? 0 : 1; d[i, j] = System.Math.Min(System.Math.Min(d[i - 1, j] + 1, //Eliminacion d[i, j - 1] + 1), //Inserccion d[i - 1, j - 1] + costo); //Sustitucion } } /// Calculamos el porcentaje de cambios en la palabra. if (s.Length > t.Length) porcentaje = ((double)d[m, n] / (double)s.Length); else porcentaje = ((double)d[m, n] / (double)t.Length); return d[m, n]; }
64.2.4
Java
Implementado como una clase estática.
public class LevenshteinDistance { private static int minimum(int a, int b, int c) { if(a<=b && a<=c) { return a; } if(b<=a && b<=c) { return b; } return c; } pu- blic static int computeLevenshteinDistance(String str1, String str2) { return computeLevenshtein- Distance(str1.toCharArray(), str2.toCharArray()); } private static int computeLevenshteinDistan- ce(char [] str1, char [] str2) { int [][]distance = new int[str1.length+1][str2.length+1]; for(int i=0;i<=str1.length;i++) { distance[i][0]=i; } for(int j=0;j<=str2.length;j++) { distance[0][j]=j; } for(int i=1;i<=str1.length;i++) { for(int j=1;j<=str2.length;j++) { distance[i][j]= minimum(distance[i-1][j]+1, distan- ce[i][j-1]+1, distance[i-1][j-1]+ ((str1[i-1]==str2[j- 1])?0:1)); } } return distance[str1.length][str2.length]; } }
64.2.5 Perl
sub fastdistance { my $word1 = shift; my $word2 = shift; return 0 if $word1 eq $word2; my @d; my $len1 = length
$word1; my $len2 = length $word2; $d[0][0] = 0; for (1.. $len1) { $d[$_][0] = $_; return $_ if $_!=$len1 && substr($word1,$_) eq substr($word2,$_); } for (1.. $len2) { $d[0][$_] = $_; return $_ if $_!=$len2 && substr($word1,$_) eq substr($word2,$_); } for my $i (1.. $len1) { my $w1 = substr($word1,$i-1,1); for (1.. $len2) { $d[$i][$_] = _min($d[$i-1][$_]+1, $d[$i][$_- 1]+1, $d[$i-1][$_-1]+($w1 eq substr($word2,$_-1,1) ? 0 : 1)); } } return $d[$len1][$len2]; } sub _min { return $_[0] < $_[1] ? $_[0] < $_[2] ? $_[0] : $_[2] : $_[1] < $_[2] ? $_[1] : $_[2]; }
64.2.6 Python
def distance(str1, str2): d=dict() for i in ran-
ge(len(str1)+1): d[i]=dict() d[i][0]=i for i in ran- ge(len(str2)+1): d[0][i] = i for i in range(1, len(str1)+1):
for j in range(1, len(str2)+1): d[i][j] = min(d[i][j-1]+1,
d[i-1][j]+1, d[i-1][j-1]+(not str1[i-1] == str2[j-1]))
return d[len(str1)][len(str2)]
64.2.7 Ruby
class String def levenshtein(other) other = other.to_s dis- tance = Array.new(self.size + 1, 0) (0..self.size).each do |i| distance[i] = Array.new(other.size + 1) distance[i][0] = i end (0..other.size).each do |j| distance[0][j] = j end (1..self.size).each do |i| (1..other.size).each do |j| distan- ce[i][j] = [distance[i - 1][j] + 1, distance[i][j - 1] + 1, distance[i - 1][j - 1] + ((self[i - 1] == other[j - 1]) ? 0 : 1)].min end end distance[self.size][other.size] end end puts “casa”.levenshtein “calle”#=> 3
64.2.8 PHP
<?php $lev = levenshtein($input, $word); ?>
64.2.9 Delphi
function LevenshteinDistance(Str1, Str2: String): In- teger; var d : array of array of Integer; Len1, Len2 : Integer; i,j,cost:Integer; begin Len1:=Length(Str1); Len2:=Length(Str2); SetLength(d,Len1+1); for i := Low(d) to High(d) do SetLength(d[i],Len2+1); for i := 0 to Len1 do d[i,0]:=i; for j := 0 to Len2 do d[0,j]:=j; for i:= 1 to Len1 do for j:= 1 to Len2 do begin if Str1[i]=Str2[j] then cost:=0 else cost:=1; d[i,j]:= Min(d[i-1, j] + 1, //
64.3. APLICACIONES 125 deletion, Min(d[i, j-1] + 1, // insertion d[i-1, j-1] + cost)
// substitution ); end; Result:=d[Len1,Len2]; end;
64.2.10
VB.NET
Public Function Levenshtein(ByVal s1 As String, ByVal s2 As String) As Integer Dim coste As Integer = 0 Dim n1 As Integer = s1.Length Dim n2 As Integer = s2.Length Dim m As Integer(,) = New Integer(n1, n2) {} For i As Integer = 0 To n1 m(i, 0) = i Next For i As Integer = 1 To n2 m(0, i) = i Next For i1 As Integer = 1 To n1 For i2 As Integer = 1 To n2 coste = If((s1(i1 - 1) = s2(i2 - 1)), 0, 1) m(i1, i2) = Math.Min(Math.Min(m(i1 - 1, i2) + 1, m(i1, i2 - 1) + 1), m(i1 - 1, i2 - 1) + coste) Next Next Return m(n1, n2) End Function
64.2.11
ActionScript 3.0
public class StringUtils { public static function levenshtein(s1:String, s2:String):int { if (s1.length == 0 || s2.length == 0) return 0; var m:uint = s1.length + 1; var n:uint = s2.length + 1; var i:uint, j:uint, cost:uint; var d:Vector.<Vector.<int>> = new Vec- tor.<Vector.<int>>(); for (i = 0; i < m; i++) { d[i] = new Vector.<int>(); for (j = 0; j < n; j++) d[i][j] = 0; } for (i = 0; i < m; d[i][0] = i++) ; for (j = 0; j < n; d[0][j] = j++) ; for (i = 1; i < m; i++) { for (j = 1; j < n; j++) { cost = (s1.charAt(i - 1) == s2.charAt(j - 1)) ? 0 : 1; d[i][j] = Math.min(Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1), d[i - 1][j - 1] + cost); } } return d[m-1][n-1]; } }
64.2.12
ColdFusion
<cfscript> function levDistance(s,t) { var d = Array- New(2); var i = 1; var j = 1; var s_i = “A"; var t_j = “A"; var cost = 0; var n = len(s)+1; var m = len(t)+1; d[n][m]=0; if (n is 1) { return m; } if (m is 1) { return n; } for (i = 1; i lte n; i=i+1) { d[i][1] = i-1; } for (j = 1; j lte m; j=j+1) { d[1][j] = j-1; } for (i = 2; i lte n; i=i+1) { s_i = Mid(s,i-1,1); for (j = 2; j lte m; j=j+1) { t_j = Mid(t,j- 1,1); if (s_i is t_j) { cost = 0; } else { cost = 1; } d[i][j] = min(d[i-1][j]+1, d[i][j-1]+1); d[i][j] = min(d[i][j], d[i- 1][j-1] + cost); } } return d[n][m]; } </cfscript>
64.2.13
JavaScript (NodeJS)
function levenshtein(s1, s2) { var l1 = s1.length; var l2 = s2.length; var d = []; var c = 0; var a = 0; if(l1 == 0) return l2; if(l2 == 0) return l1; var d = new Buffer((l1 + 1) * (l2 + 1)); a = l1 + 1; for(var i = 0; i <= l1; d[i] = i++); for(var j = 0; j <= l2; d[j * a] = j++); for(var i = 1; i <= l1; i++) { for(var j = 1; j <= l2; j++) { if(s1[i - 1]
== s2[j - 1]) c = 0; else c = 1; var r = d[j * a + i - 1] + 1; var s = d[(j - 1) * a + i] + 1; var t = d[(j - 1) * a + i - 1] + c; d[j * a + i] = Math.min(Math.min(r, s), t); } } return(d[l2 * a + l1]); }
64.3 Aplicaciones
• El proyecto ASJP usa la distancia de Levenshtein
total en una lista de palabras en diferentes lenguas del mundo, para medir la “similaridad”o “cer- canía”de las mismas, esa distancia calculada puede emplearse para proponer una clasificación filogené- tica tentativa de las lenguas del mundo.*[1]
• La distancia de Damerau-Levenshtein es una ge- neralización de la distancia de Levenshtein usada por los correctores ortográficos y en la detección de fraudes en listas de datos.
64.4 Véase también
• Distancia de Damerau-Levenshtein • Algoritmo Needleman-Wunsch • Algoritmo Smith-Waterman • Algoritmo Bitap • Autómata de Levenshtein • Espacio métrico • Agrep • Ratcliff/Obershelp • Dynamic time warping • Distancia de Jaro-Winkler64.5 Referencias
DLO
La expresión DLO (Document Like Object) está amplia- mente reconocida en la literatura sobre metadatos para aludir a los documentos de Internet (texto, imagen, au- dio, video, etc.) y se utiliza para referirse a una unidad
documental o al documento digital mínimo, que for- ma parte de una colección digital, al cual se le aplican metadatos para su descripción y recuperación.
El acrónimo DLO surge en el desarrollo de metadatos delDublin Core, concretamente en el primer taller que se llevó a cabo enOhio(Estados Unidos), donde empe- zó a utilizarse para diferenciar nociones individuales que constituyen un objeto discreto, digno de una descripción individual a través demetadatos.
En el tercer taller que se realizó se amplió su significado para referirse a cualquier recurso de información es-
pecífico que se caracterizase por ser estable (es decir,
que tenía un contenido idéntico para cada usuario).
65.1 Enlaces externos
Glosario web y proyecto de investigación de efectividad de metadatos
Página oficial de Dublin Core
Capítulo 66
Driver Chain Manager
Driver Chain Manager (DCM) es una tecnología deMicrosoft que facilita instalar y trabajar con múltiples tecnologías de asistencia que utilizan la interfaz del dri- ver de la pantalla (Display Driver Interface o DDI) en un mismo equipo.
DCM permite a un programa tener conocimiento de la
existencia de las otras aplicaciones de asistencia. Permi- te evitar que se produzcan problemas entre ellas., como fallos, mal funcionamiento, que se corrompan o incluso que no funcionen.
La tecnología DCM viene instalada en Windows. La
bibliotecase actualiza cuando se actualizan las ayudas. Un programador puede utilizar DCM en sus aplicaciones programando desde Microsoft Visual Studio empleando la biblioteca MSDN.
66.1 ¿Qué hace?
DCM es un conjunto de rutinas de la biblioteca MSDN
usada por la tecnología de asistencia de Windows. Permi- te la instalación, desinstalación y mantenimiento de inter- ceptadores del driver gráfico.
Las aplicaciones de asistencia utilizan éstas rutinas para saber en que posición de la cadena de drivers deben co- locar el suyo, de manera que no interfieran con las demás aplicaciones y éstas con ella