top of page

Funciones

  • Foto del escritor: Alejandro Rivero
    Alejandro Rivero
  • 13 abr 2025
  • 7 Min. de lectura

He preparado esta demo para explicar funciones entre conjuntos, no la he currado más porque supongo que habrá cosas mejores online. Por ejemplo podria hacer alguna animación cada vez que encuentran una definicion nueva.

/* Oval container for set visualization */ .oval { border: 4px solid green; border-radius: 50%; padding: 1rem; min-height: 200px; position: relative; } /* Emoji element styling */ .emoji { cursor: pointer; display: inline-block; } /* SVG relation line styling */ .relation-line { stroke: #000; stroke-width: 2; cursor: pointer; } .disabled { opacity: 0.5; cursor: not-allowed; }

EN

Relación

A+

B+

Set A ⟶ Set B

// Default language: Spanish ('es'); available languages: 'es' and 'en' let language = 'es'; const texts = { es: { relation: "Relación", function: "Función", totalFunction: "Función total", injective: "Función inyectiva", surjective: "Función suprayectiva", bijective: "Función biyectiva", instruction: "Haz clic en la matriz para añadir o eliminar enlaces.", pairArrow: "::", functionArrow: String.fromCodePoint(0x27FC), pairListHeader: "Set A "+ String.fromCodePoint(0x27F6) +" Set B", setA: "Set A", setB: "Set B" }, en: { relation: "Relation", function: "Function", totalFunction: "Total Function", injective: "Injective Function", surjective: "Surjective Function", bijective: "Bijective Function", instruction: "Click on the matrix to add or delete links.", pairArrow: "::", functionArrow: "?", pairListHeader: "Set A ? Set B", setA: "Set A", setB: "Set B" } }; // Data management for sets and relations let setACounter = 0; let setBCounter = 0; let setA = []; let setB = []; const maxElements = 5; // Initial emojis for each setconst initialAEmojis = [ String.fromCodePoint(0x1F34E), // ? String.fromCodePoint(0x1F34C), // ? String.fromCodePoint(0x1F347), // ? String.fromCodePoint(0x1F34A) // ?];const initialBEmojis = [ String.fromCodePoint(0x1F436), // ? String.fromCodePoint(0x1F431), // ? String.fromCodePoint(0x1F42D), // ? String.fromCodePoint(0x1F439), // ? String.fromCodePoint(0x1F430) // ?]; // Initialize sets by adding initial emojis initialAEmojis.forEach(emoji => addElement('A', emoji)); initialBEmojis.forEach(emoji => addElement('B', emoji)); // Relations: array of objects { from: "A-id", to: "B-id" } let relations = []; // Add an element (emoji) into set A or B function addElement(setType, emoji = null) {const availableA = [ String.fromCodePoint(0x1F34E), // ? String.fromCodePoint(0x1F34C), // ? String.fromCodePoint(0x1F347), // ? String.fromCodePoint(0x1F34A), // ? String.fromCodePoint(0x1F95D), // ? String.fromCodePoint(0x1F34D) // ?];const availableB = [ String.fromCodePoint(0x1F436), // ? String.fromCodePoint(0x1F431), // ? String.fromCodePoint(0x1F42D), // ? String.fromCodePoint(0x1F439), // ? String.fromCodePoint(0x1F430), // ? String.fromCodePoint(0x1F98A), // ? String.fromCodePoint(0x1F43B) // ?]; if (setType === 'A') { if (setA.length >= maxElements) return; const used = setA.map(e => e.emoji); const pool = availableA.filter(e => !used.includes(e)); const chosen = emoji || (pool.length ? pool[Math.floor(Math.random() * pool.length)] : availableA[Math.floor(Math.random() * availableA.length)]); setA.push({ id: 'A-' + setACounter++, emoji: chosen }); } else if (setType === 'B') { if (setB.length >= maxElements) return; const used = setB.map(e => e.emoji); const pool = availableB.filter(e => !used.includes(e)); const chosen = emoji || (pool.length ? pool[Math.floor(Math.random() * pool.length)] : availableB[Math.floor(Math.random() * availableB.length)]); setB.push({ id: 'B-' + setBCounter++, emoji: chosen }); }} // Remove the last element from set A or B and delete any connected relations function removeElement(setType) { if (setType === 'A') { if (setA.length === 0) return; const removed = setA.pop(); relations = relations.filter(rel => rel.from !== removed.id); } else { if (setB.length === 0) return; const removed = setB.pop(); relations = relations.filter(rel => rel.to !== removed.id); } } // Render the emojis for each set in their container function renderSets() { const containerA = document.getElementById('setA-container'); const containerB = document.getElementById('setB-container'); containerA.innerHTML = ''; containerB.innerHTML = ''; setA.forEach(item => { const div = document.createElement('div'); div.textContent = item.emoji; div.className = "emoji text-2xl p-2"; div.setAttribute('data-id', item.id); // Attach drag-start handler (manual drag support) div.addEventListener('mousedown', startDrag); containerA.appendChild(div); }); setB.forEach(item => { const div = document.createElement('div'); div.textContent = item.emoji; div.className = "emoji text-2xl p-2"; div.setAttribute('data-id', item.id); containerB.appendChild(div); }); // Update add/remove button states document.getElementById('setA-add').disabled = setA.length >= maxElements; document.getElementById('setA-remove').disabled = setA.length === 0; document.getElementById('setB-add').disabled = setB.length >= maxElements; document.getElementById('setB-remove').disabled = setB.length === 0; updateButtonState(); } function updateButtonState() { ['setA-add', 'setA-remove', 'setB-add', 'setB-remove'].forEach(id => { const btn = document.getElementById(id); if (btn.disabled) btn.classList.add('opacity-50', 'cursor-not-allowed'); else btn.classList.remove('opacity-50', 'cursor-not-allowed'); }); } // Build the relation matrix as a table: rows for Set A and columns for Set B function renderMatrix() { const table = document.getElementById('relation-matrix'); table.innerHTML = ''; // Create table header with Set B emojis let headerRow = document.createElement('tr'); let emptyHeader = document.createElement('th'); emptyHeader.className = "border p-2"; headerRow.appendChild(emptyHeader); setB.forEach(item => { let th = document.createElement('th'); th.textContent = item.emoji; th.className = "border p-2"; headerRow.appendChild(th); }); table.appendChild(headerRow); // Create each row for Set A elements setA.forEach(aItem => { let row = document.createElement('tr'); let th = document.createElement('th'); th.textContent = aItem.emoji; th.className = "border p-2"; row.appendChild(th); setB.forEach(bItem => { let cell = document.createElement('td'); cell.className = "border p-2 text-center cursor-pointer"; cell.setAttribute('data-from', aItem.id); cell.setAttribute('data-to', bItem.id); // If a relation exists, display a checkmark const exists = relations.find(rel => rel.from === aItem.id && rel.to === bItem.id); cell.textContent = exists ? String.fromCodePoint(0x2714) : ""; row.appendChild(cell); }); table.appendChild(row); }); } // Render the ordered pair list function renderPairList() { const list = document.getElementById('pair-list'); list.innerHTML = ''; relations.forEach(rel => { const li = document.createElement('li'); li.textContent = getArrowForPair(rel); list.appendChild(li); }); } // Choose arrow based on whether the relation is a function or just a relation function getArrowForPair(rel) { if (isFunction()) return formatPair(rel, texts[language].functionArrow); return formatPair(rel, texts[language].pairArrow); } function formatPair(rel, arrow) { const aEmoji = setA.find(item => item.id === rel.from)?.emoji || ''; const bEmoji = setB.find(item => item.id === rel.to)?.emoji || ''; return aEmoji + " " + arrow + " " + bEmoji; } // Draw connections as SVG lines between emojis (update their positions on render and resize) function updateSvgConnections() { const svg = document.getElementById('svg-overlay'); svg.innerHTML = ''; relations.forEach(rel => { const aElem = document.querySelector(`#setA-container [data-id="${rel.from}"]`); const bElem = document.querySelector(`#setB-container [data-id="${rel.to}"]`); if (aElem && bElem) { const aRect = aElem.getBoundingClientRect(); const bRect = bElem.getBoundingClientRect(); const containerRect = document.body.getBoundingClientRect(); // Calculate centers for the emoji elements const svgRect = svg.getBoundingClientRect();// Calculate positions relative to the SVG overlay itself, not the body// calculate offset for arrow paddingconst padding = 12;const x1 = aRect.right - svgRect.left - padding;const y1 = aRect.top + aRect.height / 2 - svgRect.top;const x2 = bRect.left - svgRect.left + padding;const y2 = bRect.top + bRect.height / 2 - svgRect.top; const isFunc = isFunction();const markerId = isFunc ? 'arrowhead' : '';if (isFunc && !document.getElementById('arrowhead')) { const marker = document.createElementNS("http://www.w3.org/2000/svg", "marker"); marker.setAttribute("id", "arrowhead"); marker.setAttribute("markerWidth", "10"); marker.setAttribute("markerHeight", "7"); marker.setAttribute("refX", "10"); marker.setAttribute("refY", "3.5"); marker.setAttribute("orient", "auto"); const arrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path"); arrowPath.setAttribute("d", "M0,0 L10,3.5 L0,7 Z"); arrowPath.setAttribute("fill", "black"); marker.appendChild(arrowPath); const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs"); defs.appendChild(marker); svg.appendChild(defs);}const line = document.createElementNS("http://www.w3.org/2000/svg", "line");line.setAttribute("x1", x1);line.setAttribute("y1", y1);line.setAttribute("x2", x2);line.setAttribute("y2", y2);line.classList.add("relation-line");if (isFunc) line.setAttribute("marker-end", "url(#arrowhead)");line.setAttribute('data-from', rel.from);line.setAttribute('data-to', rel.to);line.addEventListener('click', function(e){ e.stopPropagation(); removeRelation(rel.from, rel.to);});svg.appendChild(line); } }); // Show instruction message if no relations exist document.getElementById('instruction-message').textContent = relations.length === 0 ? texts[language].instruction : ''; } function removeRelation(from, to) { relations = relations.filter(rel => !(rel.from === from && rel.to === to)); updateAll(); } // Classification functions function isFunction() { // For a function, every element in Set A maps to at most one element const mapping = {}; for (let rel of relations) { if (mapping[rel.from]) return false; mapping[rel.from] = rel.to; } return true; } function isTotalFunction() { if (!isFunction()) return false; const mapped = new Set(relations.map(r => r.from)); return setA.length > 0 && setA.every(a => mapped.has(a.id)); } function isInjective() { if (!isFunction()) return false; const targets = relations.map(r => r.to); return (new Set(targets)).size === targets.length; } function isSurjective() { if (!isFunction()) return false; const targets = new Set(relations.map(r => r.to)); return setB.length > 0 && setB.every(b => targets.has(b.id)); } function isBijective() { return isFunction() && isInjective() && isSurjective(); } // Update explanation card and dynamic header function updateExplanationSection() { const expl = document.getElementById('explanation-section'); expl.innerHTML = ''; let classifications = []; classifications.push(texts[language].relation); if (isFunction()) { classifications.push(texts[language].function); if (isTotalFunction()) classifications.push(texts[language].totalFunction); if (isInjective()) classifications.push(texts[language].injective); if (isSurjective()) classifications.push(texts[language].surjective); if (isBijective()) classifications.push(texts[language].bijective); } // Update the main header with the classification (comma separated) document.getElementById('relation-type-header').textContent = classifications.join(', '); // Build explanation paragraphs classifications.forEach(type => { let p = document.createElement('p'); p.textContent = explanationText(type); expl.appendChild(p); }); } function explanationText(type) { switch(type) { case texts[language].relation: return language === 'es' ? "Una relación es cualquier conjunto de pares ordenados entre elementos." : "A relation is any set of ordered pairs between elements of Set A and Set B."; case texts[language].function: return language === 'es' ? "Una función es una relación donde cada elemento se relaciona con uno o ninguno." : "A function is a relation where each element of Set A relates to zero or one element of Set B."; case texts[language].totalFunction: return language === 'es' ? "Una función total es una función donde cada elemento se relaciona con exactamente uno." : "A total function is a function where every element of Set A relates to exactly one element of Set B."; case texts[language].injective: return language === 'es' ? "Una función inyectiva asigna elementos distintos a elementos distintos." : "An injective function assigns distinct elements in Set A to distinct elements in Set B."; case texts[language].surjective: return language === 'es' ? "Una función suprayectiva cubre todos los elementos del conjunto de llegada." : "A surjective function covers all elements in Set B."; case texts[language].bijective: return language === 'es' ? "Una función biyectiva es inyectiva y suprayectiva simultáneamente." : "A bijective function is both injective and surjective."; default: return ""; } } // Re-render all components function updateAll() { renderSets(); renderMatrix(); renderPairList(); updateSvgConnections(); updateExplanationSection(); } // Toggle the relation in the matrix cell when clicked document.getElementById('relation-matrix').addEventListener('click', function(e) { const cell = e.target.closest('td'); if (!cell) return; const from = cell.getAttribute('data-from'); const to = cell.getAttribute('data-to'); if (!from || !to) return; const exists = relations.find(rel => rel.from === from && rel.to === to); if (exists) { relations = relations.filter(rel => !(rel.from === from && rel.to === to)); } else { relations.push({ from, to }); } updateAll(); }); // Add/remove button event listeners for Set A and Set B document.getElementById('setA-add').addEventListener('click', function() { addElement('A'); updateAll(); }); document.getElementById('setA-remove').addEventListener('click', function() { removeElement('A'); updateAll(); }); document.getElementById('setB-add').addEventListener('click', function() { addElement('B'); updateAll(); }); document.getElementById('setB-remove').addEventListener('click', function() { removeElement('B'); updateAll(); }); // Language toggle button document.getElementById('language-toggle').addEventListener('click', function() { language = language === 'es' ? 'en' : 'es'; // Update static texts document.getElementById('setA-text').textContent = texts[language].setA.replace("Set ", ""); document.getElementById('setB-text').textContent = texts[language].setB.replace("Set ", ""); document.getElementById('pair-list-header').textContent = texts[language].pairListHeader; this.textContent = language === 'es' ? "EN" : "ES"; updateAll(); }); // Drag interactions: allow dragging from an emoji in Set A to an emoji in Set B let dragLine = null; let dragStartId = null; function startDrag(e) { dragStartId = e.currentTarget.getAttribute('data-id'); const svg = document.getElementById('svg-overlay'); const svgRect = svg.getBoundingClientRect(); const startRect = e.currentTarget.getBoundingClientRect(); const startX = startRect.left + startRect.width / 2 - svgRect.left; const startY = startRect.top + startRect.height / 2 - svgRect.top; dragLine = document.createElementNS("http://www.w3.org/2000/svg", "line"); dragLine.classList.add("relation-line"); dragLine.setAttribute("x1", startX); dragLine.setAttribute("y1", startY); dragLine.setAttribute("x2", startX); dragLine.setAttribute("y2", startY); svg.appendChild(dragLine); document.addEventListener('mousemove', (event) => duringDrag(event, svgRect)); document.addEventListener('mouseup', endDrag); } function duringDrag(e, svgRect) { if (!dragLine) return; const currentX = e.clientX - svgRect.left; const currentY = e.clientY - svgRect.top; dragLine.setAttribute("x2", currentX); dragLine.setAttribute("y2", currentY);} function endDrag(e) { if (!dragLine) return; // Check if dropping on a Set B emoji const target = document.elementFromPoint(e.clientX, e.clientY); if (target && target.parentElement && target.parentElement.id === 'setB-container') { const targetId = target.getAttribute('data-id'); if (!relations.find(rel => rel.from === dragStartId && rel.to === targetId)) { relations.push({ from: dragStartId, to: targetId }); } } dragLine.remove(); dragLine = null; dragStartId = null; document.removeEventListener('mousemove', duringDrag); document.removeEventListener('mouseup', endDrag); updateAll(); } // Recalculate line positions on window resize window.addEventListener('resize', updateSvgConnections); // Initial render of all components updateAll();

Entradas recientes

Ver todo
Mass Gap from Kaluza Klein

This is just a series of proposed blog posts from chatGPT, each in separate markdown format See also https://chatgpt.com/c/6953f699-3088-832d-8e4f-9104a9264251

 
 
 
vLLM con ray a mano

#necesarioexport SSL_CERT_FILE=/fs/agustina/arivero/supercomplex/.local/lib/python3.11/site-packages/certifi/cacert.pem export RAY_NODE_MANAGER_HEARTBEAT_TIMEOUT_MILLISECONDS=20000 # 20 seconds expor

 
 
 

Comentarios


Never Miss a Post. Subscribe Now!

I'm a paragraph. Click here to add your own text and edit me. It's easy.

Thanks for submitting!

© 2035 by Kathy Schulders. Powered and secured by Wix

  • Grey Twitter Icon
bottom of page