Sök…


Anmärkningar

I VFP grupperas operatörer i följande:

  • Numeriska operatörer
  • Logiska operatörer
  • Karaktäroperatörer
  • Datum och tid Operatörer
  • Relationsoperatörer

Det finns också operatörer, implementerade som funktioner (som bitvis operation, objektjämförelse ...).

Vi kommer att undersöka varje exempel.

Numeriska operatörer

Numeriska operatörer är de enklaste och nästan samma som på andra språk.

  • +, -, * och /. Tillsats-, subtraktions-, multiplikations- och divisionsoperatörer (i VFP finns det ingen heltalsdelning, du kan konvertera ett resultat till heltal med funktioner INT (), CEILING () och FLOOR ()).
  • % Moduloperatör.
  • ^ och **. Operatörens / krafterna. De gör båda samma sak.
  • (). Gruppera operatörer.
  • Operatörer har företräde. Beställningen är:
   ( )
   ^ (or **) 
   / and *
   - and +
    ? 10 / 5 + 2 && Outputs 4
    ? 2 + 10 / 5 && Outputs 4 as well. Division has precedence.
     
    * Both multiplication and division have same precedence
    * They would be interpreted from left to right.
    ? 4 * 5 / 2 + 5 && Outputs 15
    * Use parentheses whenever you are in doubt or want to be explicit
    ? ( (4 * 5) / 2 ) + 5 && Outputs 15. Explicit grouping of operations

    ? 4 * 5^2 && ^ has precedence, this is same as 4 * (5^2) = 100.
    ? (4 + 5)^2 && Using parentheses we say add 5 to 4 (9) and then square = 81. 

Logiska operatörer

Logiska operatörer i VFP, i deras prioritetsordning är:

Operatör Beskrivning
() Parenteser, grupputtryck
INTE,! Förneka logiskt uttrycket. INTE eller! har ingen skillnad.
OCH Logiskt OCH uttryck
ELLER Logiskt ELLER uttryck
<>,! =, # Kontrollera för ojämlikhet. Alltså samma som logisk exklusiv ELLER - XOR

Historiskt, INTE, OCH ELLER skrivs som. INTE., .AND., .OR. Du kan fortfarande använda dem om du vill, men OCH, ELLER, INTE är enklare och renare.

För falskt och sant måste du använda .F. och t. bokstäver respektive. Du kan inte välja att använda F och T istället.

* Some logical variables
local llOld, llEmail  && any variable declaration implicitly initializes the variable as .F. - false
? m.llOld, m.llEmail && Prints .F. .F.

llOld   = .T.
llEmail = .F.

if ( m.llOld AND m.llEmail )
   ? 'Old AND should be emailed to'
endif
if ( m.llOld OR m.llEmail )
   ? 'Old OR should be emailed to'
endif
if ( m.llOld AND !m.llEmail ) && Same as (m.llOld AND NOT m.llEmail)
   ? 'Old BUT should NOT be emailed to'
endif

* Above code outputs
Old OR should be emailed to
Old BUT should NOT be emailed to

I VFP utvärderas logiska uttryck på ett genvägssätt. Det vill säga, om den första delen av kontrollen tillfredsställer hela resultatet, tolkas inte resten av uttrycket. Ett prov följer:

? 1 = '2' && An obvious error. It would complain operator/operand type mismatch.

* However we could use such an expression in an if and get no error
* because it is not interpreted at all 
* (VFP is dynamic and there is no compile time check)

local llProcess
llProcess = .T.

if (m.llProcess OR (1='2'))
   ? 'Should do processing'
endif

* Would output

Should do processing

* without any error because m.llProcess true means
* the whole expression would be true, thus the expression after OR 
* is not interpreted at all.

En fallgrop som fångar nybörjare är att du ibland kan behöva flera kontroller, till exempel i en SQL-fråga, som är kopplad till AND, OR-operatörer. När det finns många av dem, kan man ignorera det faktum att operatörerna har företräde (i ordning (), INTE, OCH ELLER) och tror att tolkningen skulle göras från vänster till höger i en kedja. Överväg ett exempel:

select * from myTable where !isCustomer AND debit > 5000 OR discount > 5

vad är meningen med den här frågan? Om vi uttryckligen använder gruppering parenteser står det:

((NOT isCustomer) AND debit > 5000) OR discount > 5

förenklad ser det ut som "firstExpression" ELLER (rabatt> 5). Oavsett vad avsikten var, på grund av detta ELLER skulle den välja:

alla rader som har (rabatt> 5) - och även de där det är en kund med över 5000 debitering.

Förmodligen var avsikten "ge mig de där det INTE är en kund OCH (debet är över 5000 ELLER rabatt är över 5)". Det skulle vara klart från början om vi använde parenteser:

select * from myTable where !isCustomer AND (debit > 5000 OR discount > 5)

Du kan använda men inte värt att ha parenteser för den första INTE-operatören, när operand är ett enda uttryck, det är tillräckligt läsbart med dess företräde -! IsCustomer är tydligt läst som (NOT isCustomer).

Karaktäroperatörer

Det finns bara fyra teckenoperatörer i deras prioritetsordning:

Operatör Beskrivning
() Parenteser för gruppering. Obs: VFP-dokumentation, som jag har, saknar den här. Utan detta är operatören nästan alltid värdelös.
+ Sammanfogar (sammanfogar) strängar sida vid sida.
- Sammanfogar strängar genom att flytta bakre avstånd från vänster sträng till höger strängs ände.
$ Kontrollerar om den första strängen finns i den andra.

+ är den enklaste och används också för att sammanfoga strängar på många andra språk.

local firstName, lastName
firstName = "John"
lastName  = "Smith"

? m.firstName + " " + m.lastName

Utgångar: John Smith

- är lite knepigt och inte allmänt känt. Det tar efterföljande utrymmen från vänster sträng, lägger till dessa avstånd till strängen till höger. Anta att du har en tabell med för- och efternamn och var och en av dem är 20 tecken. Vi vill sammankoppla för- och efternamn för att skapa ett fullt namn, och vi vill också att den resulterande storleken ska fixas (i detta fall 20 + 20 + 1 mellanslag = 41). Låt oss göra det att du också har en mittnamnskolumn och vi vill att det fulla namnet ser ut som "efternamn, förnamn mittnamn_______". Det är lättast att göra detta med - operatör men du bör notera tricket att använda parenteser här för gruppering så att vi får exakt vad vi vill:

* Create a cursor for our sample and fill in a few names
Create Cursor Names (firstName c(20), midName c(20), lastName c(20))

Insert Into Names (firstName, midName, lastName) Values ('Cetin','', 'Basoz')
Insert Into Names (firstName, midName, lastName) Values ('John', 'M', 'Smith')
Insert Into Names (firstName, midName, lastName) Values ('John', 'F', 'Kennedy')
Insert Into Names (firstName, midName, lastName) Values ('Tom', '', 'Hanks')

* Select with tricky - operator
Select *, ;
    lastName - (', '+firstName-(' '+midName)) As FullName ;
    from Names ;
    INTO Cursor crsNames ;
    nofilter

Browse

Och oupoten är så här:

förnamn midName efternamn fullständiga namn
Cetin Basoz Basoz, Cetin
John M Smed Smith, John M
John F Kennedy Kennedy, John F
Tom Hanks Hanks, Tom

I kolumnen FullName skjuts alla efterföljande utrymmen till slutet snyggt. Om du kontrollerar att strukturen FullName-kolumnen är 63 tecken bred (3 * 20 + 3 tecken som vi har lagt till).

Observera vikten av att gruppera parenteser (försök ta bort parenteser eller ordna på ett annat sätt).

Även om operatören kan vara frestande att använda i sådana fall, finns det en annan sida av myntet. Denna operatör är VFP-specifik och därför är SQL inte bärbar. Du kan uppnå samma resultat med denna ANSI-kompatibla SQL istället:

Select *, ;
    CAST(RTRIM(lastName) +', '+ RTRIM(firstName) +' '+ midName as char(63)) As FullName ;
    from Names ;
    INTO Cursor crsNames ;
    nofilter

Den sista operatören är $. Den kontrollerar helt enkelt om vänster sträng är en del av höger sträng.

local upcased, digits, hexDigits
upcased = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
digits  = '0123456789'
hexDigits  = m.digits + 'ABCDEF'

? 'A' $ m.upcased && .T.
? 'a' $ m.upcased && .F.
? '1' $ m.digits && .T.
? 'F' $ m.digits && .F.
? 'F' $ m.hexDigits && .T.

Viktigt: I VFP, även om du kan skriva din kod i alla fall (övre, nedre eller blandad), är strängar alltid skiftlägeskänsliga. Till exempel: "Smith" och "smith" är två distinkta värden. Eller i tabellen om det finns en landskolumn, skulle du inte hitta 'USA' om du söker i den med 'usa'. Samma sak gäller för $ operatör, "GE" $ "Tyskland" är falskt.

Personlig anmärkning: Även om du kanske gillar $ för dess enkelhet och du kanske tycker att det ofta används i Microsofts källkoder, IMHO är det av mycket litet värde. Jag tänker på många tusentals rader som jag har skrivit i min transportör, jag tror att jag skulle hitta mycket få händelser av det i min egen kod. Nästan alltid finns det ett bättre alternativ (speciellt när vänsteroperand inte är en enda karaktär och \ eller fallkänslighet är viktig).

Datum och tid operatörer

Det finns i princip två operatörer för datum, datetime-värden. + och - är överbelastade (förmodligen en C-term) för att göra matematik för datum / datetime:

Operatör Beskrivning
+ Lägger till dagar (datum) eller sekunder (datetime) till ett datum / datetime-värde.
- Hämtar skillnaden mellan två datum- / datitidsvärden. Subtraherar dagar (datum) eller sekunder (datetime) från datetime-värden.

+ är den enklaste. Den har två operander, den ena är ett datum- eller datetime-värde och den andra är ett numeriskt (även om du kanske använder valfritt numeriskt, är det ett heltal för alla praktiska ändamål).

När en av operandema är ett datum, tas numerisk operand som "dag":

? Date() + 10 && Get the date 10 days later
* VFP is leap year aware when doing date math
? Date(2016, 2, 28) + 1 && Add 1 day to Feb 28, 2016. Returns Feb 29, 2016.
? Date(2017, 2, 28) + 1 && Add 1 day to Feb 28, 2017. Returns Mar 1, 2017.

När en av operanderna är en datatid, tas numerisk operand som "andra":

Det finns 24 * 60 * 60 = 86400 sekunder på en dag

? Datetime() + 86400 && Add 1 day to current datetime. 

Lägg till 4 timmar och 15 minuter till 1 jan 2016, 2:20

? Datetime(2016, 1, 1, 14, 20, 0) + (4 * 3600 + 15 * 60) 

Utgångar fredag 1 januari 2016, 18:35:00.

Med en enkel utskrift med hjälp av?, Beror det du ser på skärmen på datuminställningarna. Om du till exempel inte har ändrat någonting är dina datuminställningar amerikansk stil (MDY), 12 timmars format (AM / PM) och århundradet visas endast med de sista två siffrorna.

Det finns en speciell symbol ^ för datum och tid som tvingar en sträng att tolkas 'strikt' som åååå / MM / dd [HH: mm: ss | hh: mm: ss tt] -format. Därför kan ^ också betraktas som datum / datetime-operatör. Tänk till exempel på att vissa data kommer från en källa i ett format som 201610082230 (ååååMMddHHmm). För att få det värdet som en giltig Datetime:

Local cSample, tSample
cSample = '201610082230'
tSample = Ctot(Transform(m.cSample, '@R ^9999/99/99 99:99'))
? Transform(m.tSample, '@YL')

Utgångar (beroende på systemets långa datuminställning):

Lördag 8 oktober 2016, kl 22:30

- används för subtraktion. Dess operander är antingen båda datum- / datitidsvärden ELLER en är ett datum / datetime och den andra är en siffra.

Låt oss börja med den enklare datum / datetime och numeriska operander (som med + operator):

När en av operandema är ett datum, tas numerisk operand som "dag":

? Date() - 10 && What was the date 10 days ago?
? Date(2016, 3, 1) - 1 && Returns Feb 29, 2016.
? Date(2017, 3, 1) - 1 && Returns Feb 28, 2017.

När en av operanderna är en datatid, tas numerisk operand som "andra":

? Datetime() - 86400 && Go back exactly one day

Få 1 timme och 30 minuter sedan från "nu":

? Datetime() - (1 * 3600 + 30 * 60) 

Den andra formen är att få skillnaden mellan två datum / datetime-värden. Operanterna är både datum eller datetime, du kan inte använda datum och datetime samtidigt (skriv typkonvertering efter behov, VFP gör inte det åt dig). Reglerna är som i + och -, operander är datum då skillnaden är i dagar , operander är datetime då skillnaden är i sekunder .

Hur många dagar till nyårsaftonen (för 2016)?

? Date(2016, 12, 31) - Date()

Hur många sekunder kvar till midnatt?

? Dtot(Date()+1) - Datetime()

I det sista exemplet använde vi en Date / Datetime-funktion, DTOT - DateToTime för att få morgondagens midnattvärde. Det finns många användbara datum / datetime-funktioner tillgängliga, vi hoppade över dem alla eftersom de tekniskt inte betraktas som operatörer (även om de fungerar på datum / datetime :) Samma gäller även andra operatörer.

Datum / datetime subtraktion undertecknas . Det är om du använder den mindre datum / datetime som första operand då resultatet skulle bli negativt. Du kan använda abs () -funktionen om du behöver få ett positivt resultat oavsett ordningsdatum / datum.

Relationsoperatörer

Av alla operatörer är relationella operatörer de mest komplexa, det är därför vi lämnade dem till slutet.

Relationsoperatörer är också kända som jämförelseoperatörer, de används för att jämföra saker.

Jämförelsesresultat är booleskt falskt eller sant.

Intressant är dock att om du kontrollerar det i VFP hjälper du bara att se en kort lista operationer och några fler rader som om det handlar om dessa operatörer.

Nåväl, komplexiteten kommer från det faktum att de fungerar på alla typer, vare sig det är en numerisk, datum, datetime, logisk eller en sträng och till och med på objekt. Dessutom kan beteendet se besvärligt ut, du får inte vad du förväntar dig om du inte vet vilka effekter resultatet har.

Låt oss börja med en lista över relationella operatörer:

Operatör Beskrivning MEST Grundprov
> Större än ? 1> 2 &&.
< Mindre än ? 1 <2 && .T.
> = Större än eller lika med ? 1> = 2 &&.
<= Mindre än eller lika med ? 1 <= 2 && .T.
= Lika med ? 1 = 1 &&. T.
== Är exakt lika med (är vettigt för strängar) ? '1' = '1' && .T.
! =, #, <> Inte lika med (alla tre operatörerna agerar på samma sätt, välj din favorit) ? 1! = 1 && .F.

Även om du kan använda dessa med alla datatyper, bör det finnas en typkompatibilitet mellan operandema. Till exempel skulle du få ett fel om du försöker jämföra ett datum med ett heltal.

Datum och datetime kan jämföras, även om de är olika typer, gör VFP omvandlingen implicit för dig.

? Date() > DateTime() && .F. 
? Date() <= DateTime() && .T. 
? Date() < DateTime() && .T. if it is not midnight

När operanderna är numeriska är alla dessa operatörer enkla och rakt fram, de fungerar som de skulle göra i matematisk uttryck.

Med de logiska operanderna. anses vara mindre än .T.

Vad vi jämför med objekt är referensen till objektet i minnet. Således är den mest använda jämförelsen att bestämma om två objektvariabler pekar på samma objekt. dvs:

local o1, o2
o1 = createobject('Label')
o2 = createobject('Label')
? m.o1 = m.o2 && is o1 and o2 the same object?
? m.o1 > m.o2 && this would work too but likely you would never use

* remember we are comparing their references in memory
* 
* They are different objects, but do they have any difference in their properties?
? CompObj(m.o1, m.o2) && .T. They are identical properties wise

Jämförelse av karaktärdatatyp, även jämförelse av strängar är den mest förvirrande i VFP. Det fungerar inte som på andra språk och / eller databaser och unikt för VFP (och kanske för något annat xBase-språk).

För många år tillbaka har jag till och med sett några riktigt avancerade medlemmar i samhället som ännu inte visste hur dessa operatörer fungerar i VFP. Så det är ganska förståeligt att små nyanser kan förvirra nybörjare lätt.

Jämförelse handlar i princip om att vara lika eller inte. Om de inte är lika, kanske vi tänker på operatörerna>, <,> =, <=, eller hur? Med strängar är det förvirrande när två strängar betraktas som lika .

Viktigt: VFP-strängar är skiftlägeskänsliga. 'A' och 'a' är två distinkta strängar. Detta är inte fallet med många databaser där standarden är att använda en känslig kollision. Till exempel i postgreSQL eller MS SQL Server på en tabell skapad med kollisionskänslig (CI) -samling:

select * from myTable where Country = 'TURKEY'

select * from myTable where Country = 'Turkey'

skulle ge samma resultat. Men i VFP får du bara de där höljet matchar. VFP to har emellertid viss sorteringsstöd och gör en okänslig jämförelse av ärenden. (Lita inte på, se nedan)

  • Om två strängar inte är lika, hittills så bra, förutsatt att du inte ändrade några standardvärden jämförs de baserat på deras ASCII-värden .

    ? 'Basoz' < 'Cetin' && is true.
    ? 'basoz' < 'Cetin' && is false.
    ? 'Cetin' < 'David' && is true.
    ? 'Çetin' < 'David' && is false.
    

Standard för sortering är 'maskin' och det är vad du får då. När du ändrar sorteringen till något annat får du jämförelsen baserad på den sorteringens sorteringsordning. Med sortering annan inställning än standardmaskin du också innebär ett fall okänslighet på jämförelse (inte litar på detta för jämlikhet):

  set collate to 'GENERAL'
  ? 'Basoz' < 'Cetin'
  ? 'basoz' < 'Cetin'
  ? 'Cetin' < 'David'
  ? 'Çetin' < 'David'

Nu är alla dessa uttryck Sanna.

Personligt råd: Samlingar i VFP har aldrig varit tillräckligt tillförlitliga. Jag föreslår att du inte använder kollationer och håller dig med standard 'MASKIN'. Om du använder kollationer, kom ihåg att kontrollera det först när du upplever något som är mycket oväntat när det gäller karaktärsdata. Jag har sett och demonstrerat att det misslyckas i många fall, men sedan slutade jag försöka använda den mycket innan VFP9-versionen, det kan vara konsekvent nu, jag vet verkligen inte.

Med tanke på att vi täckte ojämlikhetsfall med strängar, är det svårt jämställdhetsfallet. I VFP påverkar i princip två inställningar jämförelsen:

  1. SET EXACT (Standard är AV och påverkar regelbundna jämförelser - de förutom SQL)
  2. SET ANSI (Standard är AV och effekter jämförelse endast i SQL. SET EXACT har ingen effekt på jämförelser som gjorts inom SQL-frågor.

Med SET EXACT OFF, läs jämförelsen som "börjar sträng till höger med strängen till vänster"? De jämförs upp till höger strängs längd.

? "Bobby" = "B" && Bobby starts with B, so TRUE
? "Bobby" = "Bob" && Bobby starts with Bob, so TRUE
? "Bobby" = "Bob " && Bobby starts with Bob but there is a trailing space there, FALSE
? "Bobby" = "bob" && would be true with collation set to GENERAL

Observera att med regelbunden jämförelse är "Bobby" = "B" SANT, men "B" = "Bobby" är FALSE. Med andra ord är operandens plats viktig.

Med SET EXAKT PÅ strängarna måste matcha helt men deras efterföljande utrymmen ignoreras (vi ignorerar set collate här vilket också skulle göra fallkänslighet):

? "BOBBY" = "BOB" && FALSE 
? "BOBBY" = "BOBBY" && TRUE
? "BOBBY" = "BOBBY     " && TRUE 
? "BOBBY     " = "BOBBY" && TRUE

Nu med SQL-kommandon har SET EXACT ingen effekt och det skulle uppträda som SET EXACT OFF gör.

Select * from Customers where Country = 'U'

Skulle välja kunder från USA, Storbritannien vilket land som börjar med 'U'.

I SQL borde dock per definition ändra ordning på operander ge samma resultat. Således:

Select * from Customers where 'U' = Country

skulle också fungera på samma sätt (notera skillnaden från icke-SQL-kommandon).

När du vill antyda exakta matchningar är ett alternativ att slå på ANSI:

SET ANSI ON
Select * from Customers where Country = 'USA'

returnerar alla dessa kunder från USA. Observera att de efterföljande utrymmena i landsfältet ELLER till höger uttryck ignoreras. Det spelar ingen roll hur många släp på någon sida du har. Du får jämförelsen som om det gjordes som: RTRIM (Country) = RTRIM ('USA').

Även om det inte nämns i Operatörer i VFP, är en SQL- operatör LIKE. När du använder LIKE får du en exakt matchjämförelse oavsett SET ANSI-inställning (med LIKE-krafter och implicita ANSI ON-fall - det är ju en ANSI-operatör). Se dock till att det är en liten skillnad i beteende. Det skulle inte ignorera efterföljande utrymmen, om inte den totala storleken med trailers är lika med eller mindre än fältstorleken. Om till exempel fältet Land är C (10), skulle Country = 'USA' eller Country = 'USA__' fungera, men Country = 'USA___________' skulle misslyckas ( understreck betecknar ett mellanrum och den sista har mer än sju efterföljande utrymmen).

Äntligen är vi upp till den sista operatören, ==. Det betyder exakt lika och gör det möjligt att använda med strängar. En fördel är att om du använder == betyder du alltid att du vill ha exakt matchning oavsett SET EXACT eller SET ANSI-inställningar. Men se upp igen, dess beteende är annorlunda när det är ett SQL-kommando eller ett vanligt SQL-kommando.

Med SQL:

Select * from Customers where Country == 'USA'

oavsett ANSI- och EXAKTA-inställningar, vi vill bara ha alla kunder från USA. Efterföljande utrymmen på båda sidor ignoreras.

Med icke-SQL:

? m.lcString1 == m.lcString2

skulle vara sant endast om de är exakt samma, med avseende på deras hölje och längd (efterföljande utrymmen ignoreras INTE). Det utförs inte från inställningarna SET ANSI, EXACT eller COLLATE.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow