[Powered by Google Translate] Avsnitt Problem Set 2: Hacker Edition
Rob Bowden, Harvard University
Detta är CS50. CS50.TV
Så, jag Rob. Jag är en senior i Kirkland. Detta är mitt tredje år TFing CS50.
Det är första gången som vi håller på att förändras från traditionell föreläsning stil avsnitt,
där vi bara typ av översyn vad som hände i föreläsning och sedan ni ställa frågor,
nu är mycket mer problembaserad, där vi använder Spaces och -
Åh, så tanken är att gå till den länken jag skickade dig och du kommer att vara i mitt utrymme.
Har någon inte en bärbar dator? Okej.
Så vi kommer att använda detta, och vi kommer att göra problem bor i avsnitt
och diskutera dem och räkna ut vad som är fel
och jag kan dra upp några av din kod, och jag kan diskutera dina idéer.
Så har någon haft problem?
Du kan chatta på sidan, jag vet inte om vi kommer att ha anledning till det.
Nu, liksom tidigare supersection, om du var på den klassen, vet du vad det handlar om.
På alla P uppsättningar det kommer att bli dessa sektioner.
Så P-set 2, specifikationer, antar jag att du såg det på P-set 1 redan.
Men vi kan titta på P-set 2 för vad vi kommer att gå över i dag.
Och du kommer att se en del av frågorna.
Så det här kommer att vara i alla P-set, det kommer att bli en del av frågorna.
Hittills har vi sagt, "Tänk på detta en möjlighet att träna."
Du kommer inte att bli ombedd att lämna programmet.
Tanken är att dessa är tänkta att typ av hjälpa dig komma igång med problemet set.
Jag antar på Hacker upplagan, är många av dem skulle bara vara nya, intressanta saker att lära sig.
De får inte vara direkt tillämplig på problemet set.
Och just nu är vi inte ska ha du skickar dem, men i teorin,
för senare problem apparater kan du skicka dem, och därmed kan du antingen komma till avsnitt
eller titta på avsnittet för att få svar, eller så kan du bara få dem på egen hand
Om du inte känner njuter av min närvaro.
Så - jag tror det är den första.
Åh. Även under dessa delar av frågor vi också ställa frågor om shortsen.
Så jag antar att, i teorin, du ska titta på dem innan han kom till avsnittet
men det är bra om du inte gör det, vi ska gå över dem ändå.
Så vi kan börja med dessa: "Hur en while-slinga skiljer sig från en gör-while-slinga?
När är den senare särskilt användbart? "
Så någon har några -?
[Student] DO-while-slinga alltid köra minst en gång.
Ja. Så det är skillnaden. En while-slinga - Jag ska bara göra det på här - while-slinga, vi har tillstånd
här, medan en gör-tag, har du inte ett villkor tills vi kommer ner hit.
Och så, när programmet är köra, och det blir till while-slingan,
Det kontrollerar omedelbart om detta villkor är sant.
Om detta villkor inte är sant, kommer det att hoppa strax över slingan helt.
Do-while-slinga, eftersom programmet körs, blir det till "göra".
Ingenting händer på denna punkt, bara fortsätter köra.
Sedan när den träffar "medan" om villkoret är sant, det ska slingan tillbaka och göra det igen
och igen och igen tills tillståndet är inte sant och sedan bara faller igenom.
Så skillnaden är att detta kan hoppa rätt från början.
Detta utför nödvändighet en gång och sedan kan utföra fler gånger om villkoret är fortfarande sant.
Så while-slingan kommer bara göra det en gång, eller - while-slingan - vi kanske inte behöver göra det alls,
eftersom så fort vi kommer till det, om villkoret är falskt, vi hoppa bara rätt över den.
Medan gör-while-slinga, kommer vi att köra det en gång, nödvändigtvis.
Sedan, när vi kommer till tillståndet, kontrollera vi om det är sant eller falskt.
Om det är sant, vi gör det igen, om det är falskt, vi bara fortsätta gå.
Så när är den senare särskilt användbart?
Så jag kan säga att i helheten av de 4 år, 3 år, vad som helst,
som jag har programmering, jag har använt detta, liksom under 10 gånger.
Och förmodligen 5 av dem finns i CS50 när vi inför do-while-slingor.
Så när ni använde do-while-slingor?
När är det - ja?
[Student] När du försöker få användarinmatning, eller något du vill kontrollera -
Ja. Så gör-while-slingor, indata är den stora.
Det är därför de första par problem set, när du vill fråga användaren, liknande,
"Ge mig en sträng," du kan inte fortsätta tills du får den strängen.
Och så, med nödvändighet måste be om strängen minst en gång.
Men om de svarar något dåligt, så måste du loop tillbaka och fråga igen.
Men andra än användardata, är det mycket ovanligt att jag stöter på ett fall
där jag vill slinga "minst en gång", men möjligen mer.
Frågor eller -? Har någon använt en gör-while-slinga någon annanstans?
Okej. Så nästa gång det är, "Vad odeklarerat identifierare
vanligtvis ange om matas med klang? "
Så vilken typ av kod kan jag skriva för att få "svart identifierare?
[Student] att x = 2?
Så vi kan bara prova här, x = 2.
Vi ska köra - Åh, jag inte på det. Så här får vi - okej.
"Användning av odeklarerade identifierare X".
Så det är den svarta identifierare, en variabel.
Det kommer ofta kalla en variabel en identifierare.
Så det kanske inte vet det är faktiskt en variabel, det vet inte vad det är.
Så det är en identifierare.
Så varför är det odeklarerat? Ja.
Så för att vara tydlig på terminologi, deklaration av en variabel
är när du säger "int x," eller "sträng y", oavsett.
Initieringen av variabeln, eller tilldelningen av variabeln,
är när du säger "x = 2."
Så vi kan göra dessa i separata steg, int x, x = 2, och tills - vi kan ha en massa saker här -
men tills denna linje sker, X fortfarande oinitierad men den har förklarats.
Och så kan vi naturligtvis göra det i 1 rad, och nu är vi förklara och initieras.
Frågor?
Och slutligen, "Varför är Caesar Cipher inte mycket säker?"
Så först, inte vill att någon ska säga vad Caesar Cipher är?
[Student] Caesar Cipher bara är att du kartlägger, flytta dig varje bokstav,
ett visst antal bokstäver går över, och flytta tillbaka över, och det är inte mycket säker eftersom
Det finns bara 26 möjliga alternativ och du bara måste prova varje 1 av dem tills du får det.
Åh. Så ska jag upprepa?
Caesar Cipher, it's - Jag menar, kommer du att göra med det på de problem som du -
eller jag antar standardversionen av problemet uppsättning som inte är på hackare utgåva.
Så på standardversionen på problemet set, får du ett meddelande i stil med "Hej, världen"
och du har också ett antal som 6, och du tar det budskapet, och varje enskild karaktär,
du rotera den med 6 positioner i alfabetet.
Så "h" i hej skulle bli H-I-J-K-L-M-N.
Så den första bokstaven skulle vara n. Vi gör samma sak med e.
Om vi ​​har en, typ, z eller något, då vi linda tillbaka runt till "en."
Men varje karaktär får cyklat 6 tecken senare i alfabetet, och det är inte mycket säker
eftersom det bara finns 26 möjligheter för hur många sätt du kan slå en enda bokstav.
Så du kan bara prova alla 26 av dem och, förmodligen, en tillräckligt lång budskap,
endast 1 av de möjliga 26 saker kommer att vara läsbar,
och lättläst en kommer att vara det ursprungliga meddelandet.
Så det är inte ett bra sätt att kryptera något alls.
Relaterade till dessa shorts, "Vad är en funktion?"
Så vad är en funktion? Ja.
[Student] Det är som en separat del av kod som du kan ringa för att gå igenom och sedan få tillbaka värdet av vad som helst.
Ja. Så jag ska svara genom att också svara på nästa - eller upprepa med också bara svara på nästa.
Du kan använda funktioner istället för att bara kopiera och klistra in koden om och om igen.
Bara ta den koden, lägg den i en fuction, och sedan kan du bara anropa funktionen
var du har att kopiera och klistra in.
Så fungerar är användbara.
Så nu ska vi göra verkliga problem.
Den första. Så tanken på den första är, passerar du den en sträng, och oavsett -
eller står det gemener? Det står inte gemener.
Så budskapet kan vara allt, och - oh no. Det gör det.
"För enkelhets skull kan du anta att användaren kommer bara in små bokstäver och mellanslag."
Så vi ge det ett budskap med bara gemener och vi alternera
mellan stora och små - vi ändra strängen är stora och små, omväxlande.
Så innan vi ger dig en sekund att ens dyka i problemet,
vad är det första som vi måste göra?
Åh, vad jag bara klicka på? Åh, klickade jag bara på ett mail här.
Så det första vi behöver göra - jag tittar på fel en?
Är denna del av en?
Nej, de finns fortfarande där, dock.
Okej, fortfarande här.
Nu kan vi inte anta -? Ja. Här kan vi inte anta att det bara små och utrymmen.
Så nu måste vi ta itu med det faktum att bokstäverna kan vara vad vi vill att de ska vara.
Och så det första vi vill göra är att få precis meddelandet.
Vi behöver bara få en sträng, sträng s = GetString, okej.
Nu detta problem, det finns ett par sätt att göra det.
Men vi kommer att vilja använda bitvisa operatörer här.
Finns det människor som antingen inte var på supersection,
eller något, och inte vet vad bitvisa operatörer?
Eller hur de förhåller sig till ASCII på något sätt?
[Student] Jag var inte på supersection, men jag vet vad bitvisa operatörer.
Okej. Så då jag inte behöver gå över grunderna i dem, men jag ska förklara
vad vi kommer att vilja använda här.
Så A: Binär representation av kapital A är antalet 65.
Jag ska bara titta på - 41 kommer att bli 01.000.001.
Så det borde vara 65 i decimal, så detta är den binära representationen av tecknet kapital A.
Nu, den binära representationen av tecknet gemener 'a'
kommer att bli samma sak, nästan. Är det - 6, ja. Det är rätt.
Så binär kapital A binär gemena "a".
Så märker att skillnaden mellan A och "a" är denna enda bit.
Och detta råkar vara den 32 bitar, den bit som representerar antalet 32.
Och det är rimligt eftersom A är 65, "a" är 97.
Skillnaden mellan dem är 32.
Så nu vet vi att vi kan konvertera från A till "a" genom att ta en
och bitvis Oring det med - det ser ut som en 1.
Detta är en bitvis OR, med 00100000 och som kommer ge oss "en."
Och vi kan få från "a" till A genom bitvis Anding
med 11, 0 på denna plats, 11111.
Så det här kommer sedan att ge oss exakt vad "a" var, men tar ut denna individuella bitar,
så vi får 01000001, jag vet inte om jag räknade rätt.
Men denna teknik för bitvis OR-att ta sig från huvudstaden till gemener,
och bitvis-behandling för att ta sig från gemener till kapital är inte exklusivt till A.
Alla bokstäver, K vs K, Z vs z,
alla av dem är bara att skilja denna enda bit.
Och så kan du använda detta för att växla från någon liten bokstav till någon bokstav och vice versa.
Okej. Så ett enkelt sätt att komma ur denna - så istället för att
skriva ut vad 1011111 är - ett enkelt sätt att representera detta nummer, och detta är inte en
att jag gick över i supersection, men tilde (~) är en annan bitvis operatör.
Vad ~ gör är det ser på lite representation.
Låt oss ta ett nummer.
Detta är bara några binärt tal, och vad ~ gör det bara vänder alla bitarna.
Så detta var en 1, nu en 0, är ​​detta en 0, nu 1, 010.100.
Så det är allt ~ gör. Så 32 kommer att vara nummer - bli av med det -
så 32 kommer att vara nummer 00.100.000, och så ~ detta kommer att bli
detta nummer upp här att jag-behandlas "a" med.
Ser alla det? Detta är ganska vanligt, liksom när du vill ta reda på
för senare saker som vi kanske ser, när vi vill se om -
eller vi vill ha allt, varenda bit set utom 1
du brukar göra ~ av den bit som vi inte vill ha in.
Så vi inte vill att 32 bitar inställda, så vi ~ av 32.
Okej. Så att vi kan använda alla dessa här.
Okej, så det är bra om du inte är klar, ska vi sakta gå över tillsammans,
eller gå över detta, så - genom denna. Gå igenom detta.
Så vi har vår sträng och vi vill slinga över varje tecken i strängen och göra något till den.
Så hur gör vi slinga över en sträng? Vad ska vi använda?
Jag tänker inte göra det här. Ja.
Så jag har min iterator, och han sa det, men hur vet jag hur många tecken i strängen?
Strlen (s), då jag + +.
Så vad jag har gjort här är inte det bästa sättet att göra saker.
Någon som vet varför?
Eftersom du kollar språket i strängen varje gång.
Så vi kommer att vilja flytta strlen, kunde jag säga upp här, int längd = strlen (s),
och sedan gör jag <längd, och om du inte har sett det förut,
Jag kan också göra int i = 0, längd = strlen (s).
Och så detta är något att föredra, eftersom nu har jag begränsat omfattningen
av variabel längd för att just denna "för" loop, i stället för att förklara det innan
och att det finns alltid, och om du inte fånga varför det är dåligt,
eller varför originalet var dåligt, it's - börja på för slingan.
Jag kollade tillståndet. Är jag <längden s?
Så längden av s, låt oss arbeta med "hej" hela tiden.
Så längd s, H-E-L-L-O. Längd 5.
Så jag = 0, längd 5 är, så jag är inte <5, så slingan fortsätter.
Då kan vi gå igen. Vi kontrollerar villkoret. Är jag <längden hej?
Så låt oss kontrollera längden hej. ^ H-E-L-L-O. Det är 5, jag är inte <5, så vi fortsätter igen.
Så vi beräkna, vi räknar hej, för varje iteration av slingan,
även trodde att det aldrig kommer att förändras, det är alltid kommer att vara 5.
Så minns vi bara 5 på framsidan, och nu allting är bättre.
Så iteration över hela strängen.
Vad vill vi göra för varje tecken i strängen?
[Student sett obegripligt]
Ja. Så om karaktären inte är alfabetisk, då vi bara vill hoppa över den.
Eftersom vi bara bryr oss om alfabetiska bokstäver, vi kan inte kapitalisera ett nummer.
Så hur kan vi göra detta? Så vårt tillstånd, så om vi vill ha något - kolla om den är alfabetisk ordning.
Så hur kontrollerar vi detta?
[Student] Du kan bara använda funktionen är alfa.
Är det ingår i någon av dessa, eller något har liknande char.h eller något?
Låt oss inte använda är alfa-funktionen, och använd den explicita - så vi har s [i],
som är det åttonde tecknet av s, kom ihåg att en sträng är en matris av tecken,
så det åttonde tecknet av s.
Nu, om det är en bokstav, vet vi att det måste vara i ett specifikt område.
Och vad är det intervall?
Ja. Så om s [i] är ≥ 65, och s [i] är ≤ 90, vad ska jag göra istället?
Ja. Så du bör absolut aldrig ens behöver känna till ASCII-värden av något någonsin.
Aldrig tänka på siffrorna 65, 90, 97 och 102, eller vad det nu är.
Du behöver inte - 112 -? Att du inte behöver känna dem alls. Det är fel också.
Använd endast enstaka citat tecken, enda anbud konstanter. Så "A" och mindre än 90 är "Z"
Och detta är betydligt bättre - Jag vet inte från toppen av mitt huvud att Z är 90.
Jag vet från toppen av mitt huvud som "Z" är huvudstad Z.
Så länge som det är i intervallet av kapital A till huvudstaden Z, eller vi kan kontrollera gemener,
Eller om det är i intervallet ≥ "a" och ≤ z..
Så det är vårt tillstånd.
Stil för var att sätta dessa saker varierar.
Jag gör det så här.
Nu, vad vi vill göra?
Vi vet att detta brev är ett tecken, en bokstav.
Så vi måste växla mellan om detta ska nu finnas en bokstav eller en liten bokstav.
Hur håller vi reda på vilka vi vill att det ska vara?
[Student röster, obegripliga]
Så ja, men låt mig kolla.
Modul 0-2 sades, var ett förslag kastas ut, och jag håller med om det.
Utom meddelar att som - är det så? Ja.
Det är alla andra, men vi kan inte modul 2 av i, eller i mod 2, eftersom
märker att E är kapital och "a" är gemener? Men det finns ett utrymme att skilja dem?
Så de kommer att vara samma mod 2, men de är olika fall.
[Student fråga, obegripligt]
Ja. Så vi ska bara hålla räkningen.
Vi skulle också kunna göra det här om vi ville, det kan få lite otymplig
i for-slingan deklarationer, jag ska lägga upp det här.
Så int count = börjar 0.
Och så nu, ska jag räkna hur många bokstäver som vi har haft.
Så vi oundvikligen kommer att räkna + + eftersom vi hittat en annan bokstav.
Men så nu du säger om greve mod 2.
Så vad händer om räkna mod 2? Åh. Jag gör == 0 för nu.
Vi kommer också att gå över det. Så om räkna mod 2 == 0, vad?
[Studenter svar, obegripliga]
Så vi vill att det ska sluta versaler.
Det finns 2 fall, versaler och gemener är de 2 fallen.
Så om vi är i gemener vi måste göra det versaler.
Om det är versaler behöver vi inte göra någonting.
Men, det finns ett sätt - shouldn't har vänt -
att vi inte ens behöver kontrollera om det är versaler eller gemener?
Vad kan vi göra för att alltid se till att vi alltid hamnar på versaler?
Så märker vad vi gjorde för små "a", tänk om vi gjorde detta exakt samma sak till versaler A?
Har versaler En förändring, eller har värdeförändringen?
Ja. Så någon bokstav bitvis-behandlas med ~ 32 kommer att vara samma versala bokstäver
eftersom det för varje versalt tecken den 32: a bit inte är satt.
Så om vi vill ta tecknet s [i], vi vill att det ska bli gemener eller versaler.
Så om det var små, är det nu versaler, om det var versaler, är det fortfarande versaler, och det är det.
Jag sade detta i supersection: Du kan använda 32 om du vill, men jag brukar föredra att göra "en" - A,
istället för att helt enkelt 32, eftersom det kan vara någon annan bit.
Efter 32 bitar, kan det vara någon av dessa, eller skulle vi inte ha tillräckligt
nummer för att representera alla tecken.
Så om du får 32 bitar, kan det vara det 64 bitar, kan det vara det 128 bitar.
Någon av dessa bitar kan vara lite som skiljer mellan versaler och gemener.
Jag ska inte behöva veta att det är 32-bitars.
Jag kan använda denna A - A för att få den bit som skiljer sig mellan de två
utan att behöva förlita sig på det magiska numret som är 32.
Och så nu, annars räkna var udda, och så vad vill jag göra?
[Student svar, obegripligt]
[Student] Vad är det?
Jag kommer att göra det i 1 sekund.
Så nu om jag vill - jag vill vara säker karaktären nu gemener,
så jag kan eller med 32, och 32 betyder "en" - A.
Men varsel av samma resonemang som den tidigare, att om
brevet var redan gemener, sedan O-ring med 32 bara håller det gemener.
Det har inte förändrats den ursprungliga karaktären.
Men nu har jag inte undvika att säga, "Om det är gemener, bara glömma det,
om det är versaler, sedan ändra det. "
Det är mycket mer praktiskt att göra detta.
[Student] Skulle denna strategi för att subtrahera versaler från gemena arbetet om det inte vore 32?
Om det var, typ, 34 eller något?
Så måste du veta att skillnaden mellan 2 är -? >> 1 bit.
Det kan vara mer än 1 bit, så länge som alla bitarna under denna position är desamma.
Så vi behöver minst 26 tecken - eller finns det 26 karaktärer.
Så vi behöver minst 26 siffror för att representera skillnaden -
Skillnaden mellan A och "a" måste vara minst 26,
annars skulle vi inte ha representerade allt kapital nummer.
Det innebär att A, om vi börjar med 1, det kommer att använda alla dessa bitar,
alla dessa första 5 bitar, för att representera allt genom Z.
Det är därför nästa bit, eller denna bit är nästa bit av en som är valt att skilja mellan A och "en".
Det är också därför, i ASCII tabellen finns 5 symboler skiljer versaler från gemena bokstäver.
Eftersom de är symboler, extra 5 som tar upp 32 är skillnaden mellan dem.
[Student] så vi kunde göra det, eftersom ASCII är utformad på det sättet.
Ja. Men ASCII - skillnaden kan också vara båda dessa bitar.
Liksom, om A var 10000001, och "a" var 11100001 - jag glömmer, vad som helst.
Men om det vore det, så skulle vi kunna fortfarande använda 'a' - A.
Det är just nu skillnaden mellan A och "a" är fortfarande dessa 2 bitar.
Jag tror att det är skrivet 48. Är det 32 ​​+ 64? Jag tror att det är?
Det skulle fortfarande vara 2 bitar, varenda karaktär, liksom, Z och Z, K och K,
De skulle fortfarande ha exakt samma bitar som med undantag för de 2 bitar.
Så länge som det är alltid sant, oavsett om vi använder ASCII eller något annat system,
så länge som det finns bara ett visst antal bitar som är olika för varje tecken,
då fungerar bra.
Det är bara det att 32 inrättades eftersom det är den första som vi skulle kunna använda. >> Cool.
Jag brukar föredra, om du inte har sett, om blocket är bara en enda rad,
Du kan bli av med klammerparenteser, så jag brukar föredra att göra detta.
Dessutom, du vet hur vi kan göra saker som s [i] + = 1?
Du kan också göra s [i] bitvis OCH = 32.
Och bitvis OR = 32.
Också räkna mod 2 == 0.
Så kom ihåg att - Jag kommer inte skriva det - en icke-noll är sant, och 0 är falsk.
Så "om count mod 2 == 0" är detsamma som att säga "om inte räkna mod 2."
Jag förmodligen skulle ha precis vänt linjerna och sade: "Om greve mod 2,
göra det eller 1, annars gör OCH 1 ", så att jag inte behöver" inte. "
Men detta fungerar lika bra.
Och vad kan jag göra här?
Du kan kombinera dem med ternära om du ville, men då bara skulle göra det stökigare
och förmodligen svårare att läsa, så vi inte kommer att göra det.
Någon har några andra förslag?
Är det allt problemet begärt? Oh yeah.
Så bli av med dessa tomma rader, nu ska vi ut f,% s är den en för stråkar,
Vi kommer att skriva ut F, S.
Nu ska vi köra det. Gjorde jag något fel?
Det är en \ ", jag vill ha en n..
Okej. Nu ska vi köra det. Det kommer nog skrika på mig.
Strlen i string.h.
Så detta är den fina klang är det talar om för dig vad det är i,
istället för GCC som bara säger "Hej, du glömde något, jag vet inte vad det var."
Men det kommer att säga mig: "Du menade att inkludera string.h."
Så jag inte be om någonting, så det är inte att säga något.
Men vi ska göra deras exempel, "Tack 4 tillägget".
Det ser rätt. Hurra.
Så återvänder till din huvudsakliga, jag nästan aldrig göra det.
Det är valfritt. Och viktigaste är den enda funktionen för vilka det är frivilligt.
Om du inte returnera något från huvud, det förutsätts att du tänkt att returnera 0.
Frågor?
Okej. Så nu det andra problemet.
"Minns från vecka 2 andra föreläsning som byta 2 variabler värderingar genom att
dessa 2 variabler till en funktion (även om kallas swap) inte exakt fungerar, åtminstone inte utan "pekare."
Och ignorera pekare tills vi kommer till dem.
Vi vill byta 2 variabler, vi inte använder en funktion för att göra det.
Vi kommer fortfarande att göra det i huvud som det står.
Men att använda dessa 2 variabler, vill vi inte att använda en temporär variabel.
Det finns 2 sätt att göra detta.
Du kan göra det med din traditionella binära operatorer.
Så någon som vet en snabb och smutsig sätt att göra det?
Det kan faktiskt ta en minut att tänka. Om jag har -
Jag satt problemet upp som de frågar. Så om jag har 2 variabler, A, som bara är ett heltal
att de ger mig och summa variabeln B, som är en annan heltal som jag gett.
Så om jag har dessa 2 variabler, nu vill jag byta dem.
Den traditionella, med dina vanliga binära operatorer, jag menar, liksom +, -, ÷.
Inte bitvisa operatörer som verkar på binär.
Så använder -, +, ÷, och alla de.
Vi kunde byta genom att göra något som en = a + b, och b = a - b, a = a - b.
Så förnuft kontrollera, och sedan får vi se varför det fungerar.
Låt oss säga en = 7, B = 3, så a + b kommer att bli 10.
Så vi nu sätta a = 10, och vi gör b = a - b.
Så vi gör b = a - b, som kommer att bli 7, och b = a - b igen,
eller en = a - b. Som kommer att vara 10 - 7, som är 3.
Så nu, korrekt "a" var 7, b var 3, och nu b är 7 och "a" är 3.
Så denna typ av vettigt, "a" är en kombination av de 2 siffror.
Vid denna punkt, "a" är kombinationen, och sedan vi subtrahera ut den ursprungliga B,
och då vi subtrahera ut vad som var den ursprungliga "ett".
Men detta fungerar inte för alla nummer.
För att se detta, låt oss betrakta ett system, så vi brukar tänka på heltal som 32 bitar.
Låt oss arbeta med något som är bara som 4 bitar.
Förhoppningsvis kommer jag fram till ett bra exempel just nu.
Så, jag vet, det kommer att bli lätt.
Låt oss säga att våra 2 siffror är 1111, och 1111, så vi är i binärt just nu.
I verkliga decimaler, om du vill tänka på det på det sättet, a = 15 och b = 15.
Och så vi förväntar oss, när vi byter dem - de inte ens vara samma siffror,
men jag gjorde det här sättet.
Låt oss göra dem inte samma siffror. Låt oss göra 1111 och 0001.
Så en = 15 och b = 1.
Efter att vi byter dem räknar vi "a" vara 1 och b är 15.
Så vår första steget är a = a + b.
Våra siffror är bara 4 bitar bred, så "en", som är 1111, + b, som är 0001,
kommer att hamna 10000, men vi har bara 4 bitar.
Så nu a = 0.
Och nu vill vi ställa b = a - b - faktiskt, det fungerar fortfarande perfekt.
a = a - b - låt oss se om detta fungerar perfekt.
Så då b = 0 till 1, vilket fortfarande skulle vara 15, och sedan en = a - b, vilket skulle vara 1.
Kanske detta fungerar.
Jag tycker det finns en anledning det inte fungerar att använda vanlig.
Okej, så arbetar utifrån antagandet att det inte fungerar med vanliga binära operationer,
och jag kommer att leta efter - jag Google för att se om det är sant.
Så vi vill göra det med hjälp av bitvisa operatörer och ledtråden här är XOR.
Så inför XOR (^) om du inte har sett den ännu.
Det är, återigen, en bitvis så det verkar bit för bit, och it's -
Om du har bitarna 0 och 1, då detta kommer att vara 1.
Om du har bitarna 1 och 0, det blir 1, har du bitarna 0 och 0 det ska vara 0,
och om du har bitarna 1 och 1 det ska vara 0.
Så det är som OR. Om någon av bitarna är sanna, är det 1, men till skillnad från ELLER, kan det inte vara båda bitar som är sanna.
Eller skulle ha detta vara 1, skulle XOR ha detta vara 0.
Så vi kommer att vilja använda XOR här.
Tänk på det en minut, jag ska Google.
Tja, du kan inte läsa det, jag är för närvarande på XOR swap algoritmen sidan.
Förhoppningsvis kommer detta att förklara varför jag Värre -
Detta är exakt den algoritm som vi just gjorde.
Jag kan fortfarande inte se varför - Jag måste ha just plockat ett dåligt exempel,
men det här fallet där a råkade bli 0, efter att få till 5 bitar, så nu "a" är 0,
det är vad som kallas "heltalsspill."
Enligt Wikipedia "Till skillnad från XOR swap, kräver denna variation som den använder vissa metoder
att garantera att x + y inte orsakar en heltalsspill. "
Så detta har problem, det var heltalsspill, men jag gjorde något fel.
Jag är inte säker. Jag ska försöka komma med en annan.
[Student] Tja, inte heltalsspill när du försöker sätta ett nummer i det
större än mängden bitar du har tilldelat?
Ja. Vi har 4 bitar. That - vi hade 4 bitar, vi sedan försöker lägga 1 till den, så vi sluta med 5 bitar.
Men den femte biten bara blir avskurna, ja.
Det kan faktiskt -
[Student] kastar du ett fel eller gör det - skulle det kasta ett fel?
Nej Så det finns inget fel. När du kommer till församlingen nivå, en speciell bit
någonstans är satt som sagt det var ett spill, men i C du typ av bara inte hantera det.
Du faktiskt inte kan hantera det om du använder speciella monteringsanvisningar i C.
Låt oss tänka på XOR swap.
Och jag tror Wikipediaartikeln också kan ha sagt att -
Så det tog också upp modulär aritmetik, så jag antar att jag var i teorin gör modulär aritmetik
När jag sa att 0 till 1 är 15 igen.
Så det kan faktiskt - på en vanlig processor som gör 0 till 1 = 15.
Eftersom vi hamna på 0, vi subtrahera 1, så då är det sveper precis tillbaka runt till 1111.
Så denna algoritm kan faktiskt fungera, a + b, a - b, b - a, som kan vara bra.
Men det finns vissa processorer som inte gör det, och så skulle det inte vara bra i dessa specifika sådana.
XOR swap fungerar på alla datorer. Okej.
Tanken är att det ska vara samma, dock.
Där vi använder XOR att på något sätt få information om både i 1 av variablerna,
och dra sedan ut information om de enskilda variablerna igen.
Så har någon idéer / svaret?
[Student svar, obegripligt]
Så detta borde fungera, och dessutom är XOR kommutativ.
Oavsett vilken ordning dessa 2 nummer råkar vara i upp här,
detta resultat kommer att vara densamma.
Så en ^ b är b ^ en.
Du kan också se detta skrivs som en ^ = B, B ^ = a, a ^ = b igen.
Så det här är rätt, och se varför det fungerar, tänk på bitarna.
Med hjälp av en mindre antal, låt oss säga 11.001, och 01.100.
Så detta är "ett", detta är b.
Så en ^ = b.
Vi kommer att sätta "A" = till XOR dessa 2 saker.
Så en ^ 0 är 1, 1 ^ 1 är 0, 0 ^ 1 är 1, och 0 ^ 0 är 0, 1 ^ 0 är 1.
Så "a" om man tittar på decimaltal, det kommer att bli -
du kommer inte att se mycket av en relation mellan den ursprungliga "a" och den nya "A"
men titta på bitarna, "a" är nu som ett nät av information
av både den ursprungliga "a" och den ursprungliga b..
Så om vi tar b ^ a, ser vi att vi kommer att sluta på den ursprungliga "ett".
Och om vi tar det ursprungliga "a" ^ den nya en, "vi ser att vi hamnar på den ursprungliga miljarder.
Så (a ^ b) ^ b = den ursprungliga "ett".
Och (a ^ b) ^ a = ursprunglig miljarder.
Det är - ett annat sätt att se detta är något XOR själv är alltid 0.
Så 1101 ^ 1101, är alla bitarna kommer att vara densamma.
Så det aldrig kommer att bli ett fall där 1 är en 0 och den andra är 1.
Så detta är 0000.
Samma sak med det här. (A ^ b) ^ b är som en ^ (b ^ b).
(B ^ b) kommer att vara 0, a ^ 0 är bara kommer att bli "en," eftersom alla bitar är 0.
Så de enda som kommer att vara där "a" var ursprungligen en 1 - hade sådana.
Och samma idé här, jag är ganska säker på att det är också kommutativ.
Ja. Jag sa tidigare att det var kommutativ.
Den ^ 'a' och det är associativ, så nu (b ^ a) ^ en.
Och vi kan göra b ^ (a ^ a).
Och så igen, får vi den ursprungliga miljarder.
Så "a" är nu en kombination av "a" och b tillsammans.
Med vår nya combo "a" säger vi b = combo 'a' ^ den ursprungliga B, får vi den ursprungliga "ett".
Och nu a = combo "a" ^ den nya b, som var den ursprungliga - eller som nu vad som var "a" eller b.
Det är detta fall här nere. Detta är = b, gamla B.
Så nu är allt tillbaka i bytte ordning.
Om vi ​​faktiskt tittat på bitarna, b = a ^ b, kommer att XOR dessa 2,
och svaret kommer att bli det, och sedan en = a ^ b är XOR dessa 2 och svaret är.
Frågor? Okej. Så den sista är något betydligt svårare.
[Student] Jag tror att han har en fråga om det. >> Åh, förlåt.
[Student] Vad är egentligen snabbare? Om du använder den här XOR, eller är det om du deklarerar en ny variabel?
Så vad är egentligen snabbare, förklara en ny variabel eller använda XOR byta?
Svaret är, med all sannolikhet en temporär variabel.
Och det beror på att när det är sammanställt ner - så på montering nivå,
det finns inget sådant som lokala variabler eller eventuella tillfälliga variabler eller någon av det här.
De är precis som - det finns minne, och det finns register.
Register är där saker aktivt händer.
Du lägger inte 2 saker i minnet, du lägger 2 saker i register.
Och du tar saker från minnet till register för att sedan lägga till dem,
och då kanske du sätta tillbaka dem i minnet, men alla åtgärder sker i register.
Så när du använder den temporära variabeln strategi oftast vad som händer är
Dessa 2 siffror redan i register.
Och sedan från den tidpunkten, efter att du har bytt dem,
Det ska bara börja använda det andra registret.
Var du hade använt b, det ska bara använda det register som redan lagra "en."
Så det behöver inte göra något för att faktiskt göra swappen. Ja?
[Student] Men det tar också mer minne, eller hur?
Det kommer bara att ta mer minne om det behövs för att lagra den tillfälliga variabeln.
Som om du senare använda den tillfälliga variabeln igen någonstans,
då - eller du tilldela något som tillfälliga variabeln.
Så om vid vilken tidpunkt som helst "a, 'b i temp har tydliga värderingar eller något,
då det kommer att ha olika platser i minnet, men det är sant att
Det finns många lokala variabler som bara kommer att finnas i register.
I vilket fall är det aldrig i minnet, så att du aldrig slösar minne.
Okej. Sista frågan är lite mer.
Så här i CS50 apparat, det finns en ordlista.
Och anledningen till detta är att [? B66] är en stavningskontroll där du ska skriva
med hashtabeller eller försöker eller någon datastruktur.
Du kommer att skriva en stavningskontroll, och du kommer att använda denna ordbok för att göra det.
Men för detta problem kommer vi bara att se upp för att se om ett enda ord i ordlistan.
Så istället för att lagra hela ordlistan i någon datastruktur
och sedan tittar över ett helt dokument för att se om något är felstavat,
Vi vill bara hitta 1 ord. Så vi kan bara skanna över hela ordlistan
och om vi inte hittar ordet i hela ordboken, så det var inte där.
Om vi ​​skannar över hela ordboken och ser ordet, då vi är bra, vi fann det.
Det står här att vi vill börja titta på C: s fil-hantering funktion,
eftersom vi vill läsa ordlistan,
men jag kommer att ge tipset här om vilka funktioner som du bör tänka på.
Jag skriver dem på Spaces.
Så de viktigaste du vill titta på är f öppet och sedan oundvikligen stängt f,
som kommer att gå i slutet av ditt program, och f Scan f..
Du kan också använda f läsa, men du förmodligen inte vill
eftersom det - du inte sluta behöver det.
F Scan f är vad du ska använda för att skanna över ordboken.
Och så du behöver inte koda upp lösningen, bara försöka och vilja pseudokod ditt sätt
till en lösning, och sedan kommer vi att diskutera det.
Och faktiskt, eftersom jag redan gett er dessa, om du går in i någon terminal eller din apparat skal,
Jag skulle - jag brukar - om du inte har sett ännu, jag vet inte om du gjorde i klassen,
men människan, så man-sidorna, är ganska bra för att titta på ganska mycket varje funktion.
Så jag kan göra, vill, man f, skanna f..
Detta är nu information om genomsökningen f familjen av funktioner.
Jag kan också göra MAN F, öppna, och det ger mig de detaljer i denna.
Så om du vet vilken funktion du använder, eller du läser koden
och du se några funktionen och du är som, "Vad gör det då?"
Bara mannen som funktionsnamn.
Det finns ett par konstiga exempel där du kanske måste säga
liknande. Man 2 som fungerar namn, eller man 3 som funktionsnamn,
men du behöver bara göra det om människan funktionsnamn inte råkar arbeta för första gången.
[Student] Så jag läser mansidan för öppet, men jag är fortfarande förvirrad om hur man använder det och programmet.
Okej. En hel del av man-sidor är mindre än hjälpsamma.
De är mer användbar om du redan vet vad de gör
och då behöver du bara komma ihåg ordningen på argument eller något.
Eller de kan ge dig en allmän översikt, men några av dem är mycket överväldigande.
Liksom f Scan f, också. Det ger dig information om alla dessa funktioner,
och 1 rad här nere råkar säga "F Scan f läser ur strängen punkt eller ström."
Men f öppna. Så, hur skulle vi använda f öppen?
Idén om ett program som behöver göra fil I / O är att
måste du först öppna filen du vill göra saker med, och oundvikligen,
Läs saker från den filen och göra saker med dem.
F öppet är vad vi använder för att öppna filen.
Det vi får tillbaka, så vad fil vill vi öppna, det ger oss -
här står det "/ user / share / dict / ord."
Detta är den fil som vi vill öppna, och vi vill öppna den -
Vi måste uttryckligen ange om vi vill öppna den för att läsa eller om vi vill öppna den för att skriva.
Det finns ett par kombinationer och sånt, men vi vill öppna detta för läsning.
Vi vill läsa från filen.
Så vad gör detta tillbaka? Den returnerar en fil stjärna (*),
och jag ska bara visa allt i variabeln f, så *
igen, det är en pekare, men vi vill inte ta itu med pekare.
Du kan tänka på f som är f nu variabel du ska använda för att representera filen.
Så om du vill läsa från filen, du läste från F.
Om du vill stänga filen stänger du f..
Så i slutet av programmet när vi oundvikligen vill stänga filen, vad ska vi göra?
Vi vill avsluta f..
Så nu den sista filen funktion som vi kommer att vilja använda scan f, f Scan f..
Och vad som gör det skannar över filen söker ett mönster för att matcha.
Man tittar på man-sidan här ser vi int f Scan f, ignorera returvärdet för nu.
Det första argumentet är filen * ström, så det första argumentet ska vi vill passera är f.
Vi skannar över f..
Det andra argumentet är en formatsträng.
Jag kommer att ge dig en formatsträng just nu.
Jag tror att vi råkar säga 127s \ n, en hel del som är onödigt.
Tanken på vad det formatsträng är, du kan tänka på Scan f som motsatsen till utskriften f..
Så utskrift f, skriva ut f vi också använda denna typ av format parameter,
men i tryck f. vad vi gör är - låt oss titta på motsvarande.
Så ut f, och det finns faktiskt också F Skriv ut F, där det första argumentet kommer att bli f..
När du skriver ut f, kan vi säga något i stil med "tryck 127s \ n" och sedan om vi passerar det några sträng,
det kommer att skriva ut den här strängen och sedan en ny rad.
Vad 127 betyder, jag är ganska säker, men jag har aldrig begränsat mig till det,
Du skulle inte ens behöva säga '127 'i den tryckta f,
men vad det betyder är ut de första 127 tecknen.
Så jag är ganska säker på att det är fallet. Du kan Google för det.
Men i nästa är jag nästan säker det innebär att.
Så detta är skriva de första 127 tecknen, följt av en ny rad.
F skanna f nu, i stället för att titta på en variabel och skriva ut det,
det kommer att titta på några sträng och lagra mönstret i variabeln.
Låt oss verkligen använda Scan f i en annan exempel.
Så låt oss säga att vi hade en del int, x = 4,
och vi ville skapa en sträng gjord av - ville skapa strängen
det var som detta kommer upp långt senare,
något som är precis som 4.jpg.
Så detta kan vara ett program där du kommer att ha summan räknare,
Sammanfattningsvis motverka i, och du vill spara en massa bilder.
Så du vill spara i.jpg, där i är lite upprepning av din slinga.
Så hur gör vi här strängen för den JPEG?
Om du vill skriva ut 4.jpg, kan vi bara säga print f,% d.jpg,
och då skulle det skrivas ut för den JPEG.
Men om vi vill spara strängen 4.jpg använder vi skanna f..
Så sträng s - faktiskt vi Värre - tecken, röding s, låt oss gå 100.
Så jag förklarade bara några rad 100 tecken,
och det är vad vi oundvikligen kommer att lagra den JPEG i.
Så vi kommer att använda skanna f och formatet, hur vi skulle säga% d.jpg
För att skriva ut 4.jpg är formatet av denna kommer att vara% d.jpg.
Så formatet är% d.jpg, vad vi vill ersätta% d med är x,
och nu måste vi lagra den strängen någonstans.
Och där kommer vi att lagra denna sträng är i arrayen är.
Så efter denna kodrad, s, om vi skriver ut f,% s av den rörliga s,
det kommer att skriva ut 4.jpg.
Så f Scan f är samma som Scan f, förutom nu det ser över den här filen
för vad som ska lagras i talet.
Det är vad det sista argumentet kommer att bli.
Vi vill lagra - "Scan f familjen av funktioner skannar både enligt format som provat nedan.
Om någon är lagrade på platsen poäng du kan återvända - "
Nej, vi kanske vara bra. Låt mig tänka för en sekund.
Så scan f inte - vad sjutton är den funktion som gör det?
Så scan f inte kommer att ta ett heltal och göra punkt jpg.
Det kommer att [Mumbles].
Spara int variabel i sträng int C
Vad är denna variabel, eller vad är denna funktion som kallas?
Ja. That - ja. Så vad jag definierar till dig innan var S Skriv ut f,
vilket - som är mycket vettigare, varför jag sa att det var mycket mer som print f..
Scan f är fortfarande ungefär som print f, men S Skriv ut f kommer att skanna det över
och ersätta variablerna och nu lagra den i en sträng.
Istället för att skriva ut den, lagrar den i en sträng.
Så ignorera det helt. Du kan fortfarande tänka på formatet föreskrivaren som likt utskrift f..
Så nu, om vi ville göra 4.jpg sak, skulle vi göra S Skriv ut f, x av detta.
Så vad Scan f gör - vad din fråga kommer att bli?
[Student] Jag är bara förvirrad på vad vi försöker göra här
med den JPEG. Kan du förklara det 1 gång?
Så detta var - det är mindre relevent att f skanna f. nu, förhoppningsvis kommer det knyta tillbaka i någon form av väg.
Men vad jag ursprungligen hade för avsikt att visa var - detta är faktiskt direkt relevans för dessa [? F5]
Du kommer att använda S Skriv ut f, där säger att vi har 100 bilder,
och du vill läsa bild 1.jpg, 2.jpg, 3.jpg.
Så för att göra det måste du f öppna och då måste man passera i strängen som du vill öppna.
Så vi skulle vilja öppna 1.jpg, för att skapa den sträng som är 1.jpg,
vi S Skriv ut f av% d.jpg--vi inte göra för int i = 0.
i <40, i + +.
Så S Skriv ut f% d.jpg av i.
Så efter denna linje, nu variabel eller matrisen s kommer att 1.jpg.
Eller 0.jpg, 1.jpg, 2.jpg. Och så att vi kan öppna i sin tur varje bild för läsning.
Så det är vad S Skriv ut f. gör. Ser du vad han skriver ut f nu gör?
[Student] Okej, så det tar - det skapar en sträng, something.jpg och sedan lagrar det.
Ja. Det skapar - det är en annan formatsträng, precis som Scan f och skriv ut f,
där det infogas alla variabler i det andra argumentet, kan vara s i motsats till i..
Kanske - jag menar, det är fallet. Men oavsett ordningen av argument är.
Det kommer att sätta alla variabler i formatsträngen
och sedan lagra i vår buffert, vi kallar det en buffert, det där vi lagrar strängen.
Så vi lagrar inuti är den riktigt-formaterad sträng, efter% d ersatts med 4.
[Student] Så om vi gjorde detta är den rörliga f bara kommer att bli omplacerad?
Ja. Så vi bör stänga ursprungliga f innan du gör detta.
Men - och sedan också, om det inte fanns en f öppna här, då skulle vi behöva säga -
Ja. Men det skulle öppna en hundra olika filer.
[Student] Men vi skulle inte kunna komma åt eller - okej.
Okej. Så skanna f, f Scan f, är typ av samma idé,
men i stället för, istället för att lagra det i en sträng, är det mer som du är nu
gå över ett sting och mönstermatchning mot den sträng och lagrar resultaten i variablerna.
Du kan använda Scan f för att tolka över något som 4.jpg, och lagra heltal 4 till X summan int.
Det är vad vi kan använda Scan f för.
F skanna f kommer att göra det på kommandoraden.
Jag är faktiskt ganska säker på att detta är vad CS50 biblioteket gör.
Så när du säger "få int," det är Scan f-ing över - scan f är hur du får indata.
F skanna f kommer att göra samma sak, men med hjälp av en fil för att skanna över.
Så här är vi skannar över denna fil.
Mönstret vi försöker matcha en viss sträng som är 127 tecken långt
följt av en ny linje
Så jag är ganska säker på att vi skulle till och med bara säga "matcha s" eftersom ordlistan
Vi råkar ha, vi garanterat inget ord är så länge,
och även f scan f, tror jag, kommer att stanna vid den nya linjen oavsett vad.
Men vi inkludera den nya linjen i matchen, och -
[Student] Om vi ​​inte inkludera den nya linjen, skulle det inte finna delar av ett ord?
Det - varje - att titta på ordlistan -
Så i ordboken, dessa är alla våra ord.
Var och en är på en ny rad.
Skanningen f kommer att plocka upp detta ord.
Om vi ​​inte inkluderar den nya linjen, då är det möjligt att nästa scan f bara kommer att läsa den nya linjen.
Men inklusive nya linjen kommer då att bara ignorera den nya linjen.
Men vi kommer aldrig att få del av ett ord, eftersom vi alltid läser upp till en ny rad, oavsett vad.
[Student] Men om du söker efter ordet "cissa," som cissa.
Kommer det att hitta det, och säger att det är en match?
Så här är vi - det kommer att läsa i - det är faktiskt en bra poäng.
Vi kommer aldrig att använda den nuvarande - ordet vi letar efter är den första kommandoraden argument.
Så sträng, ord = argv 1.
Så strängen vi letar efter är argv 1.
Vi är inte ute efter ett ord på alla i vår scan f..
Vad vi gjorde med Scan f blir varje ord i ordlistan,
och sedan när vi har det ord vi ska använda strcmp att jämföra dem.
Vi kommer att jämföra vårt ord och vad vi just läst i.
Så oundvikligen kommer vi att hamna gör en massa skanning fs
tills det råkar vara så att skanning f kommer tillbaka -
det kommer att återvända en, så länge som det har matchade ett nytt ord,
och det kommer tillbaka något annat så snart den har misslyckats med att matcha ord.
Vi läser hela ordboken, lagring rad för rad varje ord i den variabla talet.
Sedan vi jämför ord med s, och om jämförelsen == 0,
strcmp råkar få 0 om en match gjordes.
Så om det var 0, då kan vi trycka f, matchas,
eller ord i ordlistan, eller vad du vill skriva ut f..
Och sedan - vi vill inte f Stäng om och om igen.
Detta är den typ av sak vi vill göra, och vi är inte bara ute efter ord i ordboken.
Så vi kunde göra det, om vi ville söka sin mönster, cissa, som du sa tidigare,
om vi ville titta efter det mönstret, då det skulle misslyckas i fallet
eftersom det är faktiskt inte ett ord, men ett av orden i ordlistan råkar ha det i den.
Så det skulle matcha detta ord, men denna del av ordet är inte ett ord i sig.
Men det är inte hur vi använder den, vi läser i varje ord
och sedan jämföra ordet vi har med det ordet.
Så vi alltid jämföra hela ord.
Jag kan skicka ut de färdiga lösningarna senare.
Detta är typ nästan rätt svar, tror jag.
[Student kommentar, obegripligt]
Åh, jag bli av med det innan? Char s, jag antar att vi sa 127 - Jag glömmer vad den största är.
Vi ska bara göra 128, så nu s är tillräckligt lång.
Vi behöver inte skriva ut något.
Vi kommer även att vilja måste stänga vår fil och det bör vara ungefär rätt svar.
CS50.TV